﻿//!-----------------------------------------------------------------------
//! Copyright (C) Production Corporation. All rights reserved.
//!-----------------------------------------------------------------------
//! Shared.js
//! Production AJAX Framework.

// JavaScript Extensions and AJAX Framework API.

/// <reference name="MicrosoftAjax.js" assembly="System.Web.Extensions" />
/// <reference name="MicrosoftAjaxWebForms.js" assembly="System.Web.Extensions" />
/// <reference name="AjaxControlToolkit.ExtenderBase.BaseScripts.js" assembly="AjaxControlToolkit" />
/// <reference path="AjaxControlToolkit.Common.Common.js" assembly="AjaxControlToolkit" />
$open = function(url, bounds) {
    /// <summary>
    /// 打开新的窗口。新窗口隐藏工具栏、菜单栏和状态栏，并且可以通过鼠标拖动改变窗口的大小和位置。
    /// </summary>
    /// <param name="url" type="String">新窗口的地址。</param>
    /// <param name="bounds" type="Sys.UI.Bounds">
    /// 新窗口的大小和位置。如果用户设置新打开的窗口使用 Tab 的形式则参数值无效。
    /// </param>
    /// <example>
    /// 打开一个位于屏幕中间位置的新窗口，并且指定窗口的大小
    /// <code>
    /// window.$open('http://www.mysite.com/mypage.aspx',{width:600,height:600});
    /// </code>
    /// 打开一个满屏的浏览器窗口
    /// <code>
    /// window.$open('http://www.mysite.com/mypage.aspx');
    /// </code>
    /// </example>
    /// <returns>新打开的窗口的句柄</returns>
    var width = screen.availWidth;
    if (Sys.Browser.agent == Sys.Browser.InternetExplorer) {
        width -= 12;
    } else {
        width -= 7;
    }
    var height = screen.availHeight - 38;
    var left = 0;
    var top = 0;
    if (typeof (bounds) !== 'undefined' && bounds !== null) {
        width = bounds.width || width;
        height = bounds.height || height;
        left = bounds.x || bounds.left;
        left = !left || left === 'cener' ? ((width - bounds.width) / 2) : left;
        top = bounds.y || bounds.top;
        top = !top || top === 'middle' ? ((height - bounds.height) / 2) : top;
    }
    var features = '';
    if (Sys.Browser.agent == Sys.Browser.Firefox) {
        // in fire fox 3 the location defaults disabled, forcing the presence of the Location Bar much like in IE7.
        features = "centerscreen=yes,chrome=yes,location=no,menubar=no,toolbar=no,dependent=no,status=no,directories=no,alwaysRaised=no,resizable=yes,minimizable=yes,scrollbars=yes" +
		    ",innerWidth=" + width +
		    ",innerHeight=" + height +
		    ",top=" + top +
		    ",left=" + left;
    } else {
        // in IE7 the location is forced to present, and enabled in IE6.
        features = "menubar=no,toolbar=no,status=no,titlebar=no,location=no,resizable=yes,scrollbars=yes" +
	                    ",width=" + width +
	                    ",height=" + height +
	                    ",top=" + top +
	                    ",left=" + left;
    }
    var windowObj = window.open(url, '_blank', features, false);
    // when the browser blocked the new page, return value of open method will be null.
    // especially in IE8 for default blocks every new window.
    if (windowObj) {
        windowObj.moveTo(left, top);
    }
    return windowObj;
}

if (!getElementsByClassName) {
    var getElementsByClassName = function(searchClass, node, tag) {
        if (document.getElementsByClassName) {
            return document.getElementsByClassName(searchClass)
        } else {
            node = node || document;
            tag = tag || "*";
            var classes = searchClass.split(" "),
                elements = (tag === "*" && node.all) ? node.all : node.getElementsByTagName(tag),
                patterns = [],
                returnElements = [],
                current,
                match;
            var i = classes.length;
            while (--i >= 0) {
                patterns.push(new RegExp("(^|\\s)" + classes[i] + "(\\s|$)"));
            }
            var j = elements.length;
            while (--j >= 0) {
                current = elements[j];
                match = false;
                for (var k = 0, kl = patterns.length; k < kl; k++) {
                    match = patterns[k].test(current.className);
                    if (!match) break;
                }
                if (match) returnElements.push(current);
            }
            return returnElements;
        }
    }
}

$download = function() {
    /// <param name="virtualPath" type="String" mayBeNull="false" optional="false"></param>
    /// <param name="displayName" type="String" mayBeNull="true" optional="true"></param>
    /// <param name="repositoryName" type="String" mayBeNull="true" optional="true"></param>
    var displayName, virtualPath, repositoryName;
    if (arguments.length < 1) throw Error.parameterCount('There should be at least one parameters for file virtual path.');
    virtualPath = arguments[0];
    if (arguments.length >= 2) displayName = arguments[1];
    if (arguments.length >= 3) repositoryName = arguments[2];
    var parameters = virtualPath + '|' + (displayName ? displayName : '') + '|' + (repositoryName ? repositoryName : '');
    if (!window._downloadFrame) {
        window._downloadFrame = $common.createElementFromTemplate({
            nodeName: 'IFRAME',
            visible: false
        }, document.body);
    }
    window._downloadFrame.src = "/DownLoad.axd?d=" + encodeURIComponent(parameters);
}
// 增加Google Chrome浏览器的判断
Sys.Browser.Chrome = {};
if (navigator.userAgent.indexOf(' Chrome/') > -1) {
    Sys.Browser.agent = Sys.Browser.Chrome;
    Sys.Browser.version = parseFloat(navigator.userAgent.match(/ Chrome\/(\d+\.\d+)/)[1]);
    Sys.Browser.name = 'Chrome';
}

String.empty = '';
String.isNullOrEmpty = function(value) {
    /// <param name="value" type="String" mayBeNull="true"></param>
    /// <returns type="Boolean"></returns>
    return value === null || value.toString().length === 0;
}
String.compare = function(x, y, ignoreCase) {
    if (x === null && y === null) return 0;
    if (x === undefined && y === undefined) return 0;
    if (x === null && y !== null) return -1;
    if (x !== null && y === null) return 1;
    if (x === undefined && y !== undefined) return -1;
    if (x !== undefined && y === undefined) return 1;
    if (ignoreCase) {
        x = x.toLowerCase();
        y = y.toLowerCase();
    }
    if (x > y) return 1;
    if (x < y) return -1;
    return 0;
}
String.prototype.isEmail = function() {
    /// <returns type="Boolean"></returns>
    if (arguments.length !== 0) throw Error.parameterCount();
    return /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/.test(this);
}
String.prototype.isInteger = function() {
    /// <summary>判断字符串是否为整形数字。</summary>
    /// <returns type="Boolean"></returns>
    if (arguments.length !== 0) throw Error.parameterCount();
    return /^(\+|-)?\d+$/.test(this);
}
String.prototype.isPhoneNumber = function() {
    /// <summary>判断字符串是否为电话号码。</summary>
    /// <returns type="Boolean"></returns>
    /// <example>
    /// 086-0512-68078800-6531
    /// (+86)(0512)68078800
    /// 68078800-6531
    /// 68078800(6531)
    /// 68078800
    /// </example>
    /// <remark>
    /// 国际长途拨打方式：连续拨打“00+国际区号+城市区号+当地电话号码”，例如“00 86 27 65681210”
    /// 如果没有城市号码：连续拨打“00+国际区号+当地电话号码”，例如“00 852 65681210”
    /// 如果拨打国外手机：连续拨打“00+国际区号+当地手机号码”，例如“00 86 13907199856”
    /// </remark>
    /// <seealso cref="http://telecode.027.net/%E5%9B%BD%E9%99%85%E9%95%BF%E9%80%94"/>
    if (arguments.length !== 0) throw Error.parameterCount();
    var pattern = '((\\(\\+?\\d{1,4}\\))|(\\+?\\d{1,4}(\\-|\\s)))?' // 国家代码的匹配，数字长度为1-4。格式为(086), (+86), 086-, +86-
        + '((\\(\\d{2,4}\\))|(\\d{2,4}(\\-|\\s)))?'               // 城市区号的匹配。格式与国家代码相同，数字长度为3-4。
        + '\\d+'                                             // 总机或者独立电话的号码，长度不限。
        + '(((\\-|\\s)\\d+)|(\\(\\d+\\)))?'                       // 分机号码的匹配。
    return new RegExp('^' + pattern + '$').test(this);
}
String.prototype.isNumber = function() {
    /// <summary>判断字符串是否为数字。</summary>
    /// <returns type="Boolean"></returns>
    if (arguments.length !== 0) throw Error.parameterCount();
    return /^(\+|-)?\d+(\.\d+)?$/.test(this);
}

String.prototype.isDate = function(formats) {
    /// <summary>判断字符串是否为日期。</summary>
    /// <returns type="Boolean"></returns>
    return Date.parseLocale(this, formats) !== null;
}
/**
* Encode string into Base64, as defined by RFC 4648 [http://tools.ietf.org/html/rfc4648]
* (instance method extending String object). As per RFC 4648, no newlines are added.
*
* @param utf8encode optional parameter, if set to true Unicode string is encoded to UTF8 before 
*                   conversion to base64; otherwise string is assumed to be 8-bit characters
* @return           base64-encoded string
*/
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

String.prototype.encodeBase64 = function(utf8encode) {  // http://tools.ietf.org/html/rfc4648
    utf8encode = (typeof utf8encode == 'undefined') ? false : utf8encode;
    var o1, o2, o3, bits, h1, h2, h3, h4, e = [], pad = '', c, plain, coded;

    plain = utf8encode ? this.encodeUTF8() : this;

    c = plain.length % 3;  // pad string to length of multiple of 3
    if (c > 0) { while (c++ < 3) { pad += '='; plain += '\0'; } }
    // note: doing padding here saves us doing special-case packing for trailing 1 or 2 chars

    for (c = 0; c < plain.length; c += 3) {  // pack three octets into four hexets
        o1 = plain.charCodeAt(c);
        o2 = plain.charCodeAt(c + 1);
        o3 = plain.charCodeAt(c + 2);

        bits = o1 << 16 | o2 << 8 | o3;

        h1 = bits >> 18 & 0x3f;
        h2 = bits >> 12 & 0x3f;
        h3 = bits >> 6 & 0x3f;
        h4 = bits & 0x3f;

        // use hextets to index into b64 string
        e[c / 3] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
    }
    coded = e.join('');  // join() is far faster than repeated string concatenation

    // replace 'A's from padded nulls with '='s
    coded = coded.slice(0, coded.length - pad.length) + pad;

    return coded;
}

/**
* Decode string from Base64, as defined by RFC 4648 [http://tools.ietf.org/html/rfc4648]
* (instance method extending String object). As per RFC 4648, newlines are not catered for.
*
* @param utf8decode optional parameter, if set to true UTF8 string is decoded back to Unicode  
*                   after conversion from base64
* @return           decoded string
*/
String.prototype.decodeBase64 = function(utf8decode) {
    utf8decode = (typeof utf8decode == 'undefined') ? false : utf8decode;
    var o1, o2, o3, h1, h2, h3, h4, bits, d = [], plain, coded;

    coded = utf8decode ? this.decodeUTF8() : this;

    for (var c = 0; c < coded.length; c += 4) {  // unpack four hexets into three octets
        h1 = b64.indexOf(coded.charAt(c));
        h2 = b64.indexOf(coded.charAt(c + 1));
        h3 = b64.indexOf(coded.charAt(c + 2));
        h4 = b64.indexOf(coded.charAt(c + 3));

        bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;

        o1 = bits >>> 16 & 0xff;
        o2 = bits >>> 8 & 0xff;
        o3 = bits & 0xff;

        d[c / 4] = String.fromCharCode(o1, o2, o3);
        // check for padding
        if (h4 == 0x40) d[c / 4] = String.fromCharCode(o1, o2);
        if (h3 == 0x40) d[c / 4] = String.fromCharCode(o1);
    }
    plain = d.join('');  // join() is far faster than repeated string concatenation

    return utf8decode ? plain.decodeUTF8() : plain;
}

/**
* Encode multi-byte Unicode string into utf-8 multiple single-byte characters 
* (BMP / basic multilingual plane only) (instance method extending String object).
*
* Chars in range U+0080 - U+07FF are encoded in 2 chars, U+0800 - U+FFFF in 3 chars
*
* @return encoded string
*/
String.prototype.encodeUTF8 = function() {
    // use regular expressions & String.replace callback function for better efficiency 
    // than procedural approaches
    var str = this.replace(
      /[\u0080-\u07ff]/g,  // U+0080 - U+07FF => 2 bytes 110yyyyy, 10zzzzzz
      function(c) {
          var cc = c.charCodeAt(0);
          return String.fromCharCode(0xc0 | cc >> 6, 0x80 | cc & 0x3f);
      }
    );
    str = str.replace(
      /[\u0800-\uffff]/g,  // U+0800 - U+FFFF => 3 bytes 1110xxxx, 10yyyyyy, 10zzzzzz
      function(c) {
          var cc = c.charCodeAt(0);
          return String.fromCharCode(0xe0 | cc >> 12, 0x80 | cc >> 6 & 0x3F, 0x80 | cc & 0x3f);
      }
    );
    return str;
}

/**
* Decode utf-8 encoded string back into multi-byte Unicode characters
* (instance method extending String object).
*
* @return decoded string
*/
String.prototype.decodeUTF8 = function() {
    var str = this.replace(
      /[\u00c0-\u00df][\u0080-\u00bf]/g,                 // 2-byte chars
      function(c) {  // (note parentheses for precence)
          var cc = (c.charCodeAt(0) & 0x1f) << 6 | c.charCodeAt(1) & 0x3f;
          return String.fromCharCode(cc);
      }
    );
    str = str.replace(
      /[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g,  // 3-byte chars
      function(c) {  // (note parentheses for precence)
          var cc = ((c.charCodeAt(0) & 0x0f) << 12) | ((c.charCodeAt(1) & 0x3f) << 6) | (c.charCodeAt(2) & 0x3f);
          return String.fromCharCode(cc);
      }
    );
    return str;
}

if (!String.prototype.padLeft) {
    String.prototype.padLeft = function(padChar, length) {
        /// <summary>
        /// Pads string with specified character to a specified length from the left.
        /// </summary>
        /// <returns type="String"></returns>
        var padString = new Sys.StringBuilder();
        for (var i = this.length; i < length; i++) {
            padString.append(padChar.toString());
        }
        padString.append(this);
        return padString.toString();
    }
}
if (!String.prototype.padRight) {
    String.prototype.padRight = function(padChar, length) {
        /// <summary>
        /// Pads string with specified character to a specified length from the right.
        /// </summary>
        /// <returns type="String"></returns>
        var padString = new Sys.StringBuilder(this);
        for (var i = this.length; i < length; i++) {
            padString.append(padChar.toString());
        }
        return padString.toString();
    }
}
var $break = {};
Array.forEach = function(array, action, context, startIndex, endIndex) {
    /// <summary>
    /// 遍历数组中的元素并执行 <paramref name="action"/>。
    /// </summary>
    /// <param name="array" type="Array" mayBeNull="false">
    /// 被遍历的数组。
    /// </param>
    /// <param name="action" type="Function" mayBeNull="false">
    /// 遍历过程中所执行的回调函数。
    /// </param>
    /// <param name="context" type="Object" optional="true" mayBeNull="true">
    /// 遍历过程中传递给回调函数的上下文对象。
    /// </param>
    /// <example>
    /// <code>
    /// var array=[1,3,7];
    /// Array.forEach(array, function(item){
    ///     alert(item);
    /// })
    /// </code>
    // </example>
    try {
        startIndex = startIndex || 0;
        endIndex = endIndex || (array.length - 1);
        for (var i = startIndex; i <= endIndex; i++) {
            action(array[i], i, context);
        }
    } catch (e) {
        if (e != $break) throw e;
    }
}
Array.map = function(array, action, context, startIndex, endIndex) {
    /// <summary>
    /// 将数组中的元素进行映射为返回映射后的数组。
    /// </summary>
    /// <param name="array" type="Array" mayBeNull="false">
    /// 被映射的数组。
    /// </param>
    /// <param name="action" type="Function" mayBeNull="false">
    /// 映射过程中所执行的回调函数。
    /// </param>
    /// <param name="context" type="Object" optional="true" mayBeNull="true">
    /// 映射过程中传递给回调函数的上下文对象。
    /// </param>
    /// <returns type="Array">映射后的数组。</returns>
    /// <example>
    /// 将数组元素值加倍。
    /// <code>
    /// var array=[1,3,7];
    /// var ret = Array.map(array, function(item){
    ///     return item * 2;
    /// })
    /// </code>
    // </example>
    var ret = [];
    Array.forEach(array, function(item, index) {
        var value = action(item, index, context);
        if (value != null) {
            Array.add(ret, value);
        }
    }, startIndex, endIndex);
    return ret;
}
Array.aggregate = function(array, action, context, startIndex, endIndex) {
    /// <summary>
    /// 将数组中的元素进行汇总操作。
    /// </summary>
    /// <param name="array" type="Array" mayBeNull="false">
    /// 被汇总的数组。
    /// </param>
    /// <param name="action" type="Function" mayBeNull="false">
    /// 汇总过程中所执行的回调函数。
    /// </param>
    /// <param name="context" type="Object" optional="true" mayBeNull="true">
    /// 汇总过程中传递给回调函数的上下文对象。
    /// </param>
    /// <returns type="Object">汇总后的值。</returns>
    /// <example>
    /// 求数组元素值的总和。
    /// <code>
    /// var array=[1,3,7];
    /// var ret = Array.aggregate(array, function(sum, item){
    ///     return sum + item;
    /// });
    /// </code>
    /// 求数组的最大值
    /// <code>
    /// var array=[1,3,7];
    /// var ret = Array.aggregate(array, function(result, item){
    ///     return Math.max(result, item);
    /// });
    /// </code>
    // </example>
    var s = null;
    startIndex = startIndex || 0;
    endIndex = endIndex || (array.length - 1);
    if (array.length > startIndex) s = array[startIndex];
    for (var i = startIndex + 1; i <= endIndex; i++) {
        s = action(s, array[i], context);
    }
    return s;
}
Array.select = Array.findAll = function(array, predicate, context, startIndex, endIndex) {
    /// <summary>
    /// 查找数组中所有满足条件的元素。
    /// </summary>
    /// <param name="array" type="Array" mayBeNull="false">
    /// 被查找的源数组。
    /// </param>
    /// <param name="predicate" type="Function" mayBeNull="false">
    /// 查找过程中所执行的判断函数。
    /// </param>
    /// <param name="context" type="Object" optional="true" mayBeNull="true">
    /// 判断过程中传递给回调函数的上下文对象。
    /// </param>
    /// <returns type="Array">满足条件的元素的数组。</returns>
    /// <example>
    /// 查找数组中所有的奇数。
    /// <code>
    /// var array=[1,3,6];
    /// var ret = Array.select(array, function(item){
    ///     return item % 2 == 1;
    /// })
    /// // returns [1, 3];
    /// </code>
    // </example>
    var results = [];
    Array.forEach(array, function(value, index) {
        if (predicate(value, index, context))
            results.push(value);
    }, startIndex, endIndex);
    return results;
}
Array.find = function(array, predicate, context, startIndex, endIndex) {
    /// <summary>
    /// 查找数组中第一个满足条件的元素值。
    /// </summary>
    /// <param name="array" type="Array" mayBeNull="false">
    /// 被查找的源数组。
    /// </param>
    /// <param name="predicate" type="Function" mayBeNull="false">
    /// 查找过程中所执行的判断函数。
    /// </param>
    /// <param name="context" type="Object" optional="true" mayBeNull="true">
    /// 判断过程中传递给回调函数的上下文对象。
    /// </param>
    /// <returns type="Array">第一个满足条件的元素值，如果没有满足条件的元素则返回null。</returns>
    var index = Array.findIndex(array, predicate, context, startIndex, endIndex);
    return index === -1 ? null : array[index];
}

Array.findIndex = function(array, predicate, context, startIndex, endIndex) {
    /// <summary>
    /// 查找数组中第一个满足条件的索引。
    /// </summary>
    /// <param name="array" type="Array" mayBeNull="false">
    /// 被查找的源数组。
    /// </param>
    /// <param name="predicate" type="Function" mayBeNull="false">
    /// 查找过程中所执行的判断函数。
    /// </param>
    /// <param name="context" type="Object" optional="true" mayBeNull="true">
    /// 判断过程中传递给回调函数的上下文对象。
    /// </param>
    /// <returns type="Array">第一个满足条件的索引，如果没有满足条件的元素则返回 -1。</returns>
    var index = -1;
    Array.forEach(array, function(value, i) {
        if (predicate(value, i, context)) {
            index = i;
            throw $break;
        }
    }, context, startIndex, endIndex);
    return index;
}

Array.findLast = function(array, predicate, context, startIndex, endIndex) {
    /// <summary>
    /// 查找数组中最后一个满足条件的元素值。
    /// </summary>
    /// <param name="array" type="Array" mayBeNull="false">
    /// 被查找的源数组。
    /// </param>
    /// <param name="predicate" type="Function" mayBeNull="false">
    /// 查找过程中所执行的判断函数。
    /// </param>
    /// <param name="context" type="Object" optional="true" mayBeNull="true">
    /// 判断过程中传递给回调函数的上下文对象。
    /// </param>
    /// <returns type="Array">最后一个满足条件的元素值，如果没有满足条件的元素则返回null。</returns>
    var index = Array.findLastIndex(array, predicate, context, startIndex, endIndex);
    return index === -1 ? null : array[index];
}

Array.findLastIndex = function(array, predicate, context, startIndex, endIndex) {
    /// <summary>
    /// 查找数组中最后一个满足条件的索引。
    /// </summary>
    /// <param name="array" type="Array" mayBeNull="false">
    /// 被查找的源数组。
    /// </param>
    /// <param name="predicate" type="Function" mayBeNull="false">
    /// 查找过程中所执行的判断函数。
    /// </param>
    /// <param name="context" type="Object" optional="true" mayBeNull="true">
    /// 判断过程中传递给回调函数的上下文对象。
    /// </param>
    /// <returns type="Array">最后一个满足条件的索引，如果没有满足条件的元素则返回 -1。</returns>
    var index = -1;
    startIndex = startIndex || 0;
    endIndex = endIndex || (array.length - 1);
    for (var i = endIndex; i >= startIndex; i--) {
        if (predicate.call(value, i, context)) {
            index = i;
            break;
        }
    }
    return index;
}
Array.exists = function(array, predicate, context, startIndex, endIndex) {
    /// <summary>
    /// 判断数组中是否包含满足条件的元素。
    /// </summary>
    /// <param name="array" type="Array" mayBeNull="false">
    /// 被查找的源数组。
    /// </param>
    /// <param name="predicate" type="Function" mayBeNull="false">
    /// 查找过程中所执行的判断函数。
    /// </param>
    /// <param name="context" type="Object" optional="true" mayBeNull="true">
    /// 判断过程中传递给回调函数的上下文对象。
    /// </param>
    /// <returns type="Boolean"></returns>
    return Array.findIndex(array, predicate, context, startIndex, endIndex) !== -1;
}
$array = Array.from = function(iterable) {
    /// <summary>
    /// 将可遍历对象转成数组以便于进行进一步的操作。
    /// </summary>
    /// <param name="iterable" type="Object" mayBeNull="false">
    /// 可遍历的对象。
    /// </param>
    if (!iterable) return [];
    if (typeof (iterable) === 'array') {
        return Array.clone(iterable);
    }
    var length = iterable.length || 0, results = new Array(length);
    while (length--) results[length] = iterable[length];
    return results;
}

parseBool = Boolean.parse;

$try = function() {
    /// <summary>
    /// 尝试执行若干个函数，返回第一个没有出现异常的函数的结果。
    /// </summary>
    var value;
    for (var i = 0, length = arguments.length; i < length; i++) {
        var lambda = arguments[i];
        try {
            value = lambda();
            break;
        } catch (e) { }
    }
    return value;
}

$floor = function(x, y) {
    /// <summary>
    /// 将数字保留指定的位数并舍弃尾数。
    /// </summary>
    /// <param name="x" type="Number" mayBeNull="false">
    /// 被舍入的数字。
    /// </param>
    /// <param name="y" type="Number" mayBeNull="false">
    /// 保留的小数位数。
    /// </param>
    /// <example>
    /// $floor(2.13,1);// returns 2.1
    // </example>
    var factor = Math.pow(10, y);
    return Math.floor(x * factor) / factor;
}

$ceil = function(x, y) {
    /// <summary>
    /// 将数字保留指定的位数并且在尾数不为零的情况下进位。
    /// </summary>
    /// <param name="x" type="Number" mayBeNull="false">
    /// 被舍入的数字。
    /// </param>
    /// <param name="y" type="Number" mayBeNull="false">
    /// 保留的小数位数。
    /// </param>
    /// <example>
    /// $ceil(2.13,1);// returns 2.2
    /// $ceil(2.10,1);// returns 2.1
    // </example>
    var factor = Math.pow(10, y);
    return Math.ceil(x * factor) / factor;
}

$round = function(x, y) {
    /// <summary>
    /// 将数字保留指定的位数并对尾数使用银行家算法（四舍六入五成双）进行舍入。
    /// </summary>
    /// <param name="x" type="Number" mayBeNull="false">
    /// 被舍入的数字。
    /// </param>
    /// <param name="y" type="Number" mayBeNull="false">
    /// 保留的小数位数。
    /// </param>
    /// <example>
    /// $round(2.13,1);// returns 2.1
    /// $round(2.16,1);// returns 2.2
    /// $round(2.152,1);// returns 2.2
    /// $round(2.151,1);// returns 2.1
    // </example>
    var factor = Math.pow(10, y);
    return Math.round(x * factor) / factor;
}

$max = function() {
    /// <summary>
    /// 计算几个数值中的最大值。
    /// </summary>
    /// <example>
    /// $max(1.2,2.2.5); // returns 2.5
    // </example>
    var maxValue = arguments.length > 0 ? arguments[0] : null;
    Array.forEach($array(arguments), function(item) {
        maxValue = Math.max(item, maxValue);
    });
}
$min = function() {
    /// <summary>
    /// 计算几个数值中的最小值。
    /// </summary>
    /// <example>
    /// $min(1.2,2.2.5); // returns 1.2
    // </example>
    var minValue = arguments.length > 0 ? arguments[0] : null;
    Array.forEach($array(arguments), function(item) {
        if (item < minValue) {
            minValue = item;
        }
    });
}

$createObject = function(type, properties, events) {
    if (!type) {
        throw Error.argument('type'); // todo: support exception message
    }
    var obj = new type();
    for (var name in properties) {
        var val = properties[name];
        var getter = obj["get_" + name];
        var setter = obj["set_" + name];

        if (typeof (setter) === 'function') {
            setter.apply(obj, [val]);
        } else if (val instanceof Array) {
            // 如果不存在set方法，get方法的返回值为数组的情况下，直接将数值附加到该数组中。
            var current = getter.apply(obj);
            if (!(current instanceof Array)) throw new Error.invalidOperation(String.format('\'{0}\' is not an Array property.', name));
            for (var i = 0, j = current.length, l = val.length; i < l; i++, j++) {
                current[j] = val[i];
            }
        } else {
            throw new Error.invalidOperation(String.format('\'{0}\' is not a writable property.', name));
        }
    }
    if (events) {
        for (var name in events) {
            if (!(obj["add_" + name] instanceof Function)) throw new Error.invalidOperation(String.format('\'{0}\' is not an event.', name));
            if (!(events[name] instanceof Function)) throw new Error.invalidOperation('Handler must be a function.');
            obj["add_" + name](events[name]);
        }
    }
    // 如果存在初始化方法，则在创建对象完成之后执行该方法。
    // Todo: 使对象在窗口卸载的时候执行dispose方法。
    if (obj.initialize) {
        obj.initialize();
    }
    return obj;
}

$prompt = function(caption, value, callback, context) {
    if (window.top == window) {
        var promptObj = new Page.Prompt();
        promptObj.initialize();
        promptObj.show(caption, value, function(sender, result) {
            window.setTimeout(function() {
                if (!promptObj.get_isShown()) {
                    promptObj.dispose();
                }
            });
            if (callback) {
                return callback(sender, result, context);
            }
        });
    } else {
        window.top.$prompt(caption, value, callback, context);
    }
}

$alert = function(message, callback, title, context) {
    /// <summary>
    /// 显示提示消息。
    /// </summary>
    /// <param name="message" type="String" mayBeNull="false">
    /// 消息内容
    /// </param>
    /// <param name="callback" type="Function" optional="true" mayBeNull="true">
    /// 消息窗口关闭之后的回调函数
    /// </param>
    /// <param name="title" type="String" optional="true" mayBeNull="true">
    /// 窗口的标题。如果忽略此参数值则显示默认的标题"消息"。
    /// </param>
    /// <param name="context" optional="true" mayBeNull="true">
    /// 向回调函数传递的参数。
    /// </param>
    /// <example>
    /// <code>
    /// $alert('保存成功！',function(){
    ///     window.$close();
    /// },'保存');
    /// <code>
    /// <example>
    if (window.top == window) {
        if (title === null || title === undefined) {
            title = 'Information';
        }
        if (!window._messageObj) {
            window._messageObj = new Page.Message();
            window._messageObj.initialize();
        }
        window._messageObj.show('Information', message, callback ? function() { callback(context); } : null, title);
    } else {
        window.top.$alert(message, callback, title, context);
    }
}
$error = function(message, callback, title, context) {
    /// <summary>
    /// 显示错误提示消息。
    /// </summary>
    /// <param name="message" type="String" mayBeNull="false">消息内容</param>
    /// <param name="callback" type="Function" optional="true" mayBeNull="true">消息窗口关闭之后的回调函数</param>
    /// <param name="title" type="String" optional="true" mayBeNull="true">窗口的标题。如果忽略此参数值则显示默认的标题"错误"。</param>
    /// <param name="context" optional="true" mayBeNull="true">向回调函数传递的参数。</param>
    if (window.top == window) {
        if (title === null || title === undefined) {
            title = 'Error';
        }
        if (!window._messageObj) {
            window._messageObj = new Page.Message();
            window._messageObj.initialize();
        }
        window._messageObj.show('Error', message, callback ? function() { callback(context); } : null, title);
    } else {
        window.top.$error(message, callback, title, context);
    }
}
$confirm = function(message, callback, title, context) {
    /// <summary>
    /// 显示确认提示窗口。
    /// </summary>
    /// <param name="message" type="String" mayBeNull="false">消息内容。</param>
    /// <param name="callback" type="Function" optional="true" mayBeNull="true">消息窗口关闭之后的回调函数。</param>
    /// <param name="title" type="String" optional="true" mayBeNull="true">窗口的标题。如果忽略此参数值则显示默认的标题"确认"。</param>
    /// <param name="context" optional="true" mayBeNull="true">向回调函数传递的参数。</param>
    /// <example>
    /// <code>
    /// $confirm('确认删除记录？',function(result){
    ///     if(result){
    ///         deleteSelectedRecord();
    ///     }
    /// },'删除');
    /// <code>
    /// <example>
    if (window.top == window) {
        if (title === null || title === undefined) {
            title = 'Confirm';
        }
        if (!window._messageObj) {
            window._messageObj = new Page.Message();
            window._messageObj.initialize();
        }
        window._messageObj.show('Confirm', message, callback ? function(result) { callback(result, context); } : null, title);
    } else {
        window.top.$confirm(message, callback, title, context);
    }
};
$success = function(message, callback, title, context) {
    /// <summary>
    /// 显示成功提示消息。
    /// </summary>
    /// <param name="message" type="String" mayBeNull="false" optional="false">消息内容</param>
    /// <param name="callback" type="Function" optional="true" mayBeNull="true">消息窗口关闭之后的回调函数</param>
    /// <param name="title" type="String" optional="true" mayBeNull="true">窗口的标题。如果忽略此参数值则显示默认的标题"成功"。</param>
    /// <param name="context" optional="true" mayBeNull="true">向回调函数传递的参数。</param>
    if (window.top == window) {
        if (title === null || title === undefined) {
            title = 'Success';
        }
        if (!window._messageObj) {
            window._messageObj = new Page.Message();
            window._messageObj.initialize();
        }
        window._messageObj.show('Success', message, callback ? function() { callback(context); } : null, title);
    } else {
        window.top.$success(message, callback, title, context);
    }
};

$dialog = function(url, bounds, callback, dialogArguments, context) {
    /// <summary>
    /// 显示模态窗口。
    /// </summary>
    /// <param name="url" type="String" mayBeNull="false">新窗口的地址。</param>
    /// <param name="bounds" type="Sys.UI.Bounds" optional="true" mayBeNull="true">新窗口的大小和位置。</param>
    /// <param name="callback" type="Function" optional="true" mayBeNull="true">模态窗口关闭之后的回调函数。</param>
    /// <param name="dialogArguments" optional="true" mayBeNull="true">向模态窗口传递的参数。可以是除了 DomElement 以外的任意对象。</param>
    /// <param name="context" optional="true" mayBeNull="true">向回调函数传递的参数。</param>
    if (window.top == window) {
        var dialogObj = new Page.ModalDialog();
        dialogObj.initialize();
        dialogObj.show(url, bounds, function(result) {
            // 延迟销毁窗口对象的操作，由于窗口对象销毁之后如果仍然有程序在运行并且引用窗口内部的HTML Element的话，就会出现错误。
            window.setTimeout(function() {
                dialogObj.dispose();
            });
            if (callback) callback(result, context);
        }, dialogArguments);
    } else {
        window.top.$dialog(url, bounds, callback, dialogArguments, context);
    }
}

$close = function() {
    /// <summary>
    /// 关闭窗口。
    /// </summary>
    if (window.top == window) {
        window.close();
    } else {
        var dialog = window.top.Page.Dialog.find(function(item) {
            return Object.getType(item) == window.top.Page.ModalDialog && item._contentFrame.contentWindow == window;
        });
        if (dialog) {
            dialog.hide();
        }
    }
}

$text = function(element, text) {
    /// <summary>
    /// 设置或获取DOM元素显示的文本。
    /// </summary>
    /// <param name="element" domElement="true" optional="false" mayBeNull="false"></param>
    /// <param name="text" mayBeNull="true" optional="true" type="String">设置的文本内容。如果忽略此参数则为获取文本内容。</param>
    if (typeof (text) != 'undefined') {
        text = text == null ? '' : text;
        if (element.nodeType == Page.NodeType.element) {
            switch (element.nodeName.toUpperCase()) {
                case 'INPUT':
                    if (element.type == 'text' || element.type == 'submit' || element.type == 'reset' || element.type == 'button' || element.type == 'hidden') {
                        element.value = text;
                    } else {
                        Error.argument(String.format('The text of "{0}" element cannot be modified.', element.nodeName));
                    }
                    break;
                case 'TEXTAREA':
                    element.value = text;
                    break;
                case 'SELECT':
                    Error.argument('The text of "SELECT" element cannot be modified.');
                    break;
                default:
                    if (Sys.Browser.agent == Sys.Browser.InternetExplorer) {
                        element.innerText = text;
                    } else {
                        text = HTMLEncode(text);
                        text = text.replace(/\n/ig, "<BR/>");
                        element.innerHTML = text;
                    }
                    break;
            }
        } else if (element.nodeType == Page.NodeType.text) {
            element.data = text;
        }
    } else {
        if (element.nodeType == Page.NodeType.element) {
            switch (element.nodeName.toUpperCase()) {
                case 'INPUT':
                    if (element.type == 'text' || element.type == 'submit' || element.type == 'reset' || element.type == 'button' || element.type == 'hidden') {
                        return element.value;
                    } else {
                        return null;
                    }
                    break;
                case 'TEXTAREA':
                    return element.value;
                    break;
                case 'SELECT':
                    if (!element.multiple) {
                        return $text(element.options[element.selectedIndex]);
                    } else {
                        var text = [];
                        for (var i = 0, I = element.options.length; i < I; j++) {
                            var option = element.options[i];
                            if (option.selected) {
                                Array.add(text, $text(option));
                            }
                        }
                        return text;
                    }
                    break;
                default:
                    if (Sys.Browser.agent == Sys.Browser.InternetExplorer) {
                        return element.innerText;
                    } else {
                        return element.textContent;
                    }
                    break;
            }
        } else if (element.nodeType == Page.NodeType.text) {
            return element.data;
        }
    }
    function HTMLEncode(html) {
        var temp = document.createElement("div");
        (temp.textContent != null) ? (temp.textContent = html) : (temp.innerText = html);
        var output = temp.innerHTML;
        temp = null;
        return output;
    }
}

$attr = function(element, args) {
    if (typeof (args) === 'string') {
        return eval('element.' + args) || element.getAttribute(args);
    } else {
        for (var key in args) {
            element.setAttribute(key, args[key]);
        }
    }
}
$removeAttribute = function(element, attrName) {
    if (element.removeAttribute) {
        element.removeAttribute(attrName);
    } else {
        element.removeProperty(attrName);
    }
}


$ready = function(handler) {
    Page.ready(handler);
}

_Page = function() {
    this._cancelButtonID = null;
    this._cancelButton = null;
    this._form = null;
    this._messageObj = null;
    this._mouseLocation = new Sys.UI.Point(0, 0);

    this._cancelHandler = null;
    this._document$delegates = {
        keydown: Function.createDelegate(this, this._onKeyDown)
    }
    this._keydownHandler = null;
    this._unloadHandler = null;
    this._beginRequestHandler = null;
    this._endRequestHandler = null;
    this._mouseMoveHandler = null;
    this._onSubmitHandler = null;
    this._readyHandler = null;
    this._clientStateField = "__ClientState";
    this._originalValidationSummaryOnSubmit = null;
    this._originalWebForm_DoPostBackWithOptions = null;
    this._validationOverrided = false;
    this._isReady = false;
    this._readyList = [];
};
_Page.prototype = {
    _cursorWithNames: ['default', 'pointer', 'text', 'progress', 'move', 'help', 'crosshair', 'wait',
        'n-resize', 'nw-resize', 'w-resize', 'sw-resize', 's-resize', 'se-resize', 'e-resize', 'ne-resize',
        'not-allowed', 'col-resize', 'row-resize', 'vertical-text'],
    set_cancelButtonID: function(value) {
        this._cancelButton = null;
        this._cancelButtonID = value;
    },
    get_cancelButtonID: function() {
        return this._cancelButtonID;
    },
    set_overflow: function(overflow) {
        var htmlElement = document.getElementsByTagName('html')[0];
        if (typeof (overflow) != 'undefined') {
            if (typeof (overflow) == 'string') {
                if (overflow == 'yes' || overflow == 'scroll') {
                    htmlElement.style.overflow = 'scroll';
                    document.body.style.overflow = 'scroll';
                } else {
                    htmlElement.style.overflow = 'hidden';
                    document.body.style.overflow = 'hidden';
                }
            } else if (typeof (overflow) == 'boolean') {
                htmlElement.style.overflow = overflow ? 'scroll' : 'hidden';
                document.body.style.overflow = overflow ? 'scroll' : 'hidden';
            } else {
                htmlElement.style.overflow = 'auto';
                document.body.style.overflow = 'auto';
            }
        }
    },
    get_overflow: function() {
        var htmlElement = document.getElementsByTagName('html')[0];
        return htmlElement.overflow;
    },
    getScrollBounds: function() {
        // 由于在AjaxControlToolkit中的方法存在一定的问题，因此需要进行改进
        var x = 0;
        var y = 0;
        var width = 0;
        var height = 0;
        // in Opera the client size not equals body size, this is a bug of AjaxControlToolkit
        if (Sys.Browser.agent == Sys.Browser.Opera) {
            var htmlElement = document.getElementsByTagName('html')[0];
            x = htmlElement.scrollLeft;
            y = htmlElement.scrollTop;
            width = htmlElement.clientWidth;
            height = htmlElement.clientHeight;
        } else if (Sys.Browser.agent == Sys.Browser.InternetExplorer) {
            x = document.documentElement.scrollLeft;
            y = document.documentElement.scrollTop;
            width = document.documentElement.clientWidth || document.body.offsetWidth;
            height = document.documentElement.clientHeight || document.body.offsetHeight;
        } else if (Sys.Browser.agent == Sys.Browser.Firefox) {
            // In the firefox 3.5 and above version, the value of body scroll left and scroll top cannot indicate the browser actual scroll position
            // So it should be alternatived with documentElement scroll left and scroll top.
            x = document.body.scrollLeft || document.documentElement.scrollLeft;
            y = document.body.scrollTop || document.documentElement.scrollTop;
            width = Math.min(window.innerWidth, document.documentElement.offsetWidth);
            height = Math.min(window.innerHeight, document.documentElement.offsetHeight);
        } else {// Sys.Browser.Safari, etc.
            x = document.body.scrollLeft;
            y = document.body.scrollTop;
            width = Math.min(window.innerWidth, document.documentElement.offsetWidth);
            height = Math.min(window.innerHeight, document.documentElement.offsetHeight);
        }
        return new Sys.UI.Bounds(x, y, width, height);
    },
    getMaxLevel: function(container, excludes) {
        /// <summary>
        /// 获取当前浏览器窗口可见元素中的zIndex最大值
        /// </summary>
        /// <remark>
        /// 在页面元素数太多时有可能会存在性能问题，尤其是对IE浏览器。
        /// 其他的浏览器性能相对比较高完全可以忽略此操作所带来的性能损失。
        /// </remark>
        container = container || document.body;
        var maxLevel = 0;
        for (var i = 0, I = container.childNodes.length; i < I; i++) {
            var element = container.childNodes[i];
            if (excludes && Array.contains(excludes, element)) continue;
            if (element.nodeType == 1 && $common.getVisible(element)) {
                var zIndex = 0;
                if (element.style.zIndex) {
                    if (typeof (element.style.zIndex) == String) {
                        zIndex = parseInt(element.style.zIndex);
                    } else {
                        zIndex = element.style.zIndex;
                    }
                }
                maxLevel = Math.max(maxLevel, Math.max(element.style.zIndex, this.getMaxLevel(element)));
            }
        }
        return maxLevel;
    },
    getCursorPosition: function(element) {
        var position = 0;
        if (element.setSelectionRange) {
            position = parseInt(element.selectionStart, 10);
        } else if (document.selection) {
            selection = document.selection.createRange();
            var dummy = element.createTextRange();
            for (position = 0; dummy.compareEndPoints("StartToStart", selection) < 0; position++)
                dummy.moveStart('character', 1);
        }
        return position;
    },
    setSelectionRange: function(element, selectionStart, selectionEnd) {
        if (element.setSelectionRange) {
            element.setSelectionRange(selectionStart, selectionEnd);
        } else if (element.createTextRange) {
            var range = element.createTextRange();
            range.collapse(true);
            range.moveEnd('character', selectionEnd);
            range.moveStart('character', selectionStart);
            range.select();
        }
    },
    setCursor: function(element, cursor) {
        /// <summary>
        /// 设置鼠标的样式。
        /// </summary>
        /// <param name="element" domElement="true" optional="false" mayBeNull="false"></param>
        /// <param name="text" mayBeNull="false" optional="false" type="Page.Cursor"></param>
        $common.setStyle(element, { cursor: this._cursorWithNames[cursor] })
    },
    getPositionParent: function(element) {
        if (element.style.position != 'absolute') return element.parentNode || document.documentElement;
        var tempRelativeOrAbsoluteParent = element.parentNode;
        var relativeParent = null;
        while (tempRelativeOrAbsoluteParent && (tempRelativeOrAbsoluteParent != document.documentElement)) {
            if (tempRelativeOrAbsoluteParent.style.position != 'relative') {
                tempRelativeOrAbsoluteParent = tempRelativeOrAbsoluteParent.parentNode;
            } else {
                relativeParent = tempRelativeOrAbsoluteParent;
                break;
            }
        }
        return relativeParent || document.documentElement;
    },
    getScrollOffset: function(element, side) {
        side = 'scroll' + side.substring(0, 1).toUpperCase() + side.substring(1);
        var offset = 0;
        var elt = element;
        while ((elt = Page.getPositionParent(elt)).nodeType == Page.NodeType.element) {
            offset += elt[side];
        }
        return offset;
    },
    get_isReady: function() {
        return this._isReady;
    },
    ready: function(fn) {
        // If the DOM is already ready
        if (this._isReady) {
            // Execute the function immediately
            fn();
        } else {
            // Add the function to the wait list
            this._readyList.push(fn);
        }
    },
    _onReady: function() {
        // Mozilla, Opera and webkit nightlies currently support this event
        if (document.removeEventListener) {
            document.removeEventListener("DOMContentLoaded", this._readyHandler, false);
        }
        if (Sys.Browser.agent == Sys.Browser.InternetExplorer) {
            if (document.readyState === "complete") {
                document.detachEvent("onreadystatechange", this._readyHandler);
            } else {
                return;
            }
        }
        this._isReady = true;
        // If there are functions bound, to execute
        if (this._readyList) {
            // Execute all of them
            Array.forEach(this._readyList, function(fn) { fn(); });

            // Reset the list of functions
            this._readyList = null;
        }
    },
    _onCancel: function(evt) {
        evt.preventDefault();
        window.$close();
    },
    _onKeyDown: function(evt) {
        if (evt.keyCode == 27 && this._cancelButton) {
            this._cancelButton.click();
        }
    },
    _onApplicationLoad: function() {
        this._registerCancelEvent();
        this._loadClientState();
    },
    _onBeginRequest: function(sender, args) {
    },
    _onEndRequest: function(sender, args) {
        var error = args.get_error();
        // 使用自定义的美化窗口显示服务器端异常
        // 由于异常已经在服务器端有详细的记录，此处不需要记录日志。
        if (error && $alert) {
            $alert(error.message);
            args.set_errorHandled(true);
        }
    },
    _registerCancelEvent: function() {
        if (this._cancelButtonID && !this._cancelButton) {
            this._cancelButton = $get(this._cancelButtonID);
            if (this._cancelButton) {
                this._cancelHandler = Function.createDelegate(this, this._onCancel);
                $addHandler(this._cancelButton, "click", this._cancelHandler);
            }
        }
    },
    _loadClientState: function() {
        // 在页面初始化之前增加客户端状态的加载功能
        var clientStateField = $get(this._clientStateField);
        if (window.loadClientState && clientStateField) {
            window.loadClientState(clientStateField.value);
        }
    },
    _onSubmit: function() {
        /// 在页面提交之前增加客户端状态的保存功能
        var clientStateField = $get(this._clientStateField);
        if (clientStateField && window.saveClientState) {
            clientStateField.value = window.saveClientState();
        }
        return true;
    },
    _onApplicationUnload: function() {
        // 此事件只有在PostBack，窗口关闭或刷新的情况下才会引发
        if (this._document$delegates) {
            $common.removeHandlers(document, this._document$delegates);
            this._document$delegates = null;
        }
        if (this._cancelHandler) {
            $removeHandler(this._cancelButton, "click", this._cancelHandler);
            this._cancelHandler = null;
        }
        if (typeof (Sys.WebForms) !== "undefined" && typeof (Sys.WebForms.PageRequestManager) !== "undefined") {
            var requestManager = Sys.WebForms.PageRequestManager.getInstance();
            if (this._onSubmitHandler) {
                Array.remove(requestManager._onSubmitStatements, this._onSubmitHandler);
                this._onSubmitHandler = null;
            }
            if (this._beginRequestHandler) {
                requestManager.remove_beginRequest(this._beginRequestHandler);
                this._beginRequestHandler = null;
            }
            if (this._endRequestHandler) {
                requestManager.remove_endRequest(this._endRequestHandler);
                this._endRequestHandler = null;
            }
        } else {
            if (this._onSubmitHandler) {
                $removeHandler(document.forms[0], "submit", this._onSubmitHandler);
                this._onSubmitHandler = null;
            }
        }
        if (this._unloadHandler) {
            Sys.Application.remove_unload(this._unloadHandler);
            this._unloadHandler = null;
        }
        if (window._messageObj) {
            window._messageObj.dispose();
            window._messageObj = null;
        }
    },
    _preInit: function() {
        // 当页面中没有 UpdatePanel 时则不会创建 Sys.WebForms.PageRequestManager 的实例。
        this._onSubmitHandler = Function.createDelegate(this, this._onSubmit);
        if (typeof (Sys.WebForms) !== "undefined" && typeof (Sys.WebForms.PageRequestManager) !== "undefined") {
            var requestManager = Sys.WebForms.PageRequestManager.getInstance();
            this._form = requestManager._form;

            Array.add(requestManager._onSubmitStatements, this._onSubmitHandler);

            this._beginRequestHandler = Function.createDelegate(this, this._onBeginRequest);
            requestManager.add_beginRequest(this._beginRequestHandler);

            this._endRequestHandler = Function.createDelegate(this, this._onEndRequest);
            requestManager.add_endRequest(this._endRequestHandler);
        } else {
            this._form = document.forms[0];
            $addHandler(document.forms[0], "submit", this._onSubmitHandler);
        }
        // 页面每次加载时都需要初始化操作，但是事件只需要在页面第一次加载的时候注册就可以了
        Sys.Application.add_load(Function.createDelegate(this, this._onApplicationLoad));

        this._unloadHandler = Function.createDelegate(this, this._onApplicationUnload);
        Sys.Application.add_unload(this._unloadHandler);
        // 注册页面事件
        $addHandlers(document, this._document$delegates);
        this._overrideWebForm();
        this._overrideUIValidation();
        this._fixControlToolkitBugs();

        this._readyHandler = Function.createDelegate(this, this._onReady);
        // Mozilla, Opera and webkit nightlies currently support this event
        if (document.addEventListener) {
            // Use the handy event callback
            document.addEventListener("DOMContentLoaded", this._readyHandler, false);
        } else if (document.attachEvent) {
            // ensure firing before onload,
            // maybe late but safe also for iframes
            document.attachEvent("onreadystatechange", this._readyHandler);
            // If IE and not an iframe
            // continually check to see if the document is ready
            if (document.documentElement.doScroll && self == self.top) {
                if (this._isReady) return;
                try {
                    // If IE is used, use the trick by Diego Perini
                    // http://javascript.nwbox.com/IEContentLoaded/
                    document.documentElement.doScroll("left");
                } catch (error) {
                    setTimeout(this._readyHandler, 0);
                    return;
                }

                // and execute any waiting functions
                this._onReady();
            }
        }

        // A fallback to window.onload, that will always work
        $addHandler(window, "load", this._readyHandler);
    },
    initialize: function() {
        this._preInit();
        if (typeof (window.pageInit) == 'function' && window.top == window) {
            window.pageInit();
        }
    },
    _fixControlToolkitBugs: function() {
        if (typeof ($common) !== 'undefined') {
            var common$getLocation = $common.getLocation;
            $common.getLocation = function(element) {
                /// <summary>Gets the coordinates of a DOM element.</summary>
                /// <param name="element" domElement="true"/>
                /// <returns type="Sys.UI.Point">
                ///   A Point object with two fields, x and y, which contain the pixel coordinates of the element.
                /// </returns>
                var point = common$getLocation(element);
                // The border of the viewport in IE dialog is not defined.
                // when the editor location is specified the border size 2px should be added
                // since it has been substracted in getLocation method.
                if (Sys.Browser.agent == Sys.Browser.InternetExplorer && (
                // ensure the window is dialog
                    typeof window.dialogWidth == 'string' ||
                // ensure the window is in iframe
                    window.top != window)) {
                    point = new Sys.UI.Point(point.x + 2, point.y + 2);
                }
                return point;
            }
        }
        if (typeof (AjaxControlToolkit) !== 'undefined') {
            // 解决MaskedEdit只能使用slash作为日期分隔符的问题
            if (typeof (AjaxControlToolkit.MaskedEditBehavior) !== 'undefined') {
                AjaxControlToolkit.MaskedEditBehavior.prototype.ConvFmtDate = function(input, loadFirst) {
                    var m_arrDateLD;
                    var m_arrDate;
                    //perform convert for loading of a page 
                    if (loadFirst) {
                        //split values for perform convert load 
                        m_arrDateLD = input.split(this.get_CultureDatePlaceholder());
                        //split destination value
                        m_arrDate = input.split(this.get_CultureDatePlaceholder());
                        // convert current Culture to user culture format
                        if (this.get_UserDateFormat() != AjaxControlToolkit.MaskedEditUserDateFormat.None) {
                            m_arrDate[this.get_CultureDateFormat().indexOf("D")] = m_arrDateLD[this._CultureDateFormat.indexOf("D")];
                            m_arrDate[this.get_CultureDateFormat().indexOf("M")] = m_arrDateLD[this._CultureDateFormat.indexOf("M")];
                            m_arrDate[this.get_CultureDateFormat().indexOf("Y")] = m_arrDateLD[this._CultureDateFormat.indexOf("Y")];
                        }
                    }
                    else {
                        // split value (not load) 
                        m_arrDate = input.split(this.get_CultureDatePlaceholder());
                    }
                    var m_mask = this._maskvalid;
                    if (this._MaskType == AjaxControlToolkit.MaskedEditType.DateTime) {
                        m_mask = m_mask.split(" ")[0];
                    }
                    m_mask = m_mask.split(this.get_CultureDatePlaceholder());
                    if (parseInt(m_arrDate.length, 10) != 3) {
                        return "";
                    }
                    var D = parseInt(m_arrDate[this.get_CultureDateFormat().indexOf("D")], 10) + '';
                    if (D.length < m_mask[this.get_CultureDateFormat().indexOf("D")].length) {
                        while (D.length < m_mask[this.get_CultureDateFormat().indexOf("D")].length) {
                            D = "0" + D;
                        }
                    }
                    m_arrDate[this.get_CultureDateFormat().indexOf("D")] = D;
                    var M = parseInt(m_arrDate[this.get_CultureDateFormat().indexOf("M")], 10) + '';
                    if (M.length < m_mask[this.get_CultureDateFormat().indexOf("M")].length) {
                        while (M.length < m_mask[this.get_CultureDateFormat().indexOf("M")].length) {
                            M = "0" + M;
                        }
                    }
                    m_arrDate[this.get_CultureDateFormat().indexOf("M")] = M;
                    var Y = parseInt(m_arrDate[this.get_CultureDateFormat().indexOf("Y")], 10) + '';
                    while (Y.length < m_mask[this.get_CultureDateFormat().indexOf("Y")].length) {
                        Y = "0" + Y;
                    }
                    m_arrDate[this.get_CultureDateFormat().indexOf("Y")] = Y;
                    return m_arrDate[0] + this.get_CultureDatePlaceholder() + m_arrDate[1] + this._CultureDatePlaceholder + m_arrDate[2];
                }
            }
        }
    },
    _overrideWebForm: function() {
        if (window.WebForm_DoPostBackWithOptions) {
            this._originalWebForm_DoPostBackWithOptions = window.WebForm_DoPostBackWithOptions;
            window.WebForm_DoPostBackWithOptions = Function.createDelegate(this, this._WebForm_DoPostBackWithOptions);
        }
        if (window.WebForm_CallbackComplete) {
            WebForm_CallbackComplete = function() {
                for (var i = 0; i < __pendingCallbacks.length; i++) {
                    callbackObject = __pendingCallbacks[i];
                    if (callbackObject && callbackObject.xmlRequest && (callbackObject.xmlRequest.readyState == 4)) {
                        // the callback should be executed after releasing all resources
                        // associated with this request.
                        // Originally if the callback gets executed here and the callback
                        // routine makes another ASP.NET ajax request then the pending slots and
                        // pending callbacks array gets messed up since the slot is not released
                        // before the next ASP.NET request comes.
                        // FIX: This statement has been moved below
                        // WebForm_ExecuteCallback(callbackObject);
                        if (!__pendingCallbacks[i].async) {
                            __synchronousCallBackIndex = -1;
                        }
                        __pendingCallbacks[i] = null;
                        var callbackFrameID = "__CALLBACKFRAME" + i;
                        var xmlRequestFrame = document.getElementById(callbackFrameID);
                        if (xmlRequestFrame) {
                            xmlRequestFrame.parentNode.removeChild(xmlRequestFrame);
                        }
                        // the following statement has been moved down from above;
                        WebForm_ExecuteCallback(callbackObject);
                    }
                }
            }
        }
        if (window.WebForm_FireDefaultButton) {
            WebForm_FireDefaultButton = function(event, target) {
                if (event.keyCode == 13) {
                    var src = event.srcElement || event.target;
                    if (!src || (src.tagName.toLowerCase() != "textarea")) {
                        var defaultButton;
                        if (__nonMSDOMBrowser) {
                            defaultButton = document.getElementById(target);
                        } else {
                            defaultButton = document.all[target];
                        }
                        if (defaultButton) {
                            if (typeof (defaultButton.click) != "undefined") {
                                defaultButton.click();
                                event.cancelBubble = true;
                                if (event.stopPropagation) event.stopPropagation();
                                return false;
                            } else if (typeof (defaultButton.href) != "undefined") {// for LinkButton in browsers except IE
                                eval(decodeURIComponent(defaultButton.href.substr(11)));
                                event.cancelBubble = true;
                                if (event.stopPropagation) event.stopPropagation();
                                return false;
                            }
                        }
                    }
                }
                return true;
            }
        }
    },
    _WebForm_DoPostBackWithOptions: function(options) {
        this._overrideUIValidation();
        this._originalWebForm_DoPostBackWithOptions(options);
    },
    _overrideUIValidation: function() {
        if (this._validationOverrided) return;
        if (window.ValidationSummaryOnSubmit) {
            this._originalValidationSummaryOnSubmit = window.ValidationSummaryOnSubmit;
            window.ValidationSummaryOnSubmit = Function.createDelegate(this, this._ValidationSummaryOnSubmit);
        }
        if (window.ValidatorValidate) {
            this._originalValidatorValidate = window.ValidatorValidate;
            window.ValidatorValidate = Function.createDelegate(this, this._ValidatorValidate);
        }
        if (window.Page_ClientValidate) {
            this._validationOverrided = true;
        }
    },
    _ValidatorValidate: function(val, validationGroup, event) {
        this._originalValidatorValidate(val, validationGroup, event);
        if (val.isvalid) {
            if (typeof (val.validfunction) == "function") {
                val.validfunction();
            }
        } else {
            if (typeof (val.invalidfunction) == "function") {
                val.invalidfunction();
            }
        }
    },
    _ValidationSummaryOnSubmit: function(validationGroup) {
        if (typeof (Page_ValidationSummaries) == "undefined")
            return;
        var summary, sums, s;
        for (sums = 0; sums < Page_ValidationSummaries.length; sums++) {
            summary = Page_ValidationSummaries[sums];
            summary.style.display = "none";
            if (!Page_IsValid && IsValidationGroupMatch(summary, validationGroup)) {
                var i;
                if (summary.showsummary != "False") {
                    summary.style.display = "";
                    if (typeof (summary.displaymode) != "string") {
                        summary.displaymode = "BulletList";
                    }
                    switch (summary.displaymode) {
                        case "List":
                            headerSep = "<br>";
                            first = "";
                            pre = "";
                            post = "<br>";
                            end = "";
                            break;
                        case "BulletList":
                        default:
                            headerSep = "";
                            first = "<ul>";
                            pre = "<li>";
                            post = "</li>";
                            end = "</ul>";
                            break;
                        case "SingleParagraph":
                            headerSep = " ";
                            first = "";
                            pre = "";
                            post = " ";
                            end = "<br>";
                            break;
                    }
                    s = "";
                    if (typeof (summary.headertext) == "string") {
                        s += summary.headertext + headerSep;
                    }
                    s += first;
                    for (i = 0; i < Page_Validators.length; i++) {
                        if (!Page_Validators[i].isvalid && typeof (Page_Validators[i].errormessage) == "string") {
                            s += pre + Page_Validators[i].errormessage + post;
                        }
                    }
                    s += end;
                    summary.innerHTML = s;
                    window.scrollTo(0, 0);
                }
                if (summary.showmessagebox == "True") {
                    s = "";
                    if (typeof (summary.headertext) == "string") {
                        s += summary.headertext + "\r\n";
                    }
                    var lastValIndex = Page_Validators.length - 1;
                    for (i = 0; i <= lastValIndex; i++) {
                        if (!Page_Validators[i].isvalid && typeof (Page_Validators[i].errormessage) == "string") {
                            switch (summary.displaymode) {
                                case "List":
                                    s += Page_Validators[i].errormessage;
                                    if (i < lastValIndex) {
                                        s += "\r\n";
                                    }
                                    break;
                                case "SingleParagraph":
                                    s += Page_Validators[i].errormessage + " ";
                                    break;
                                case "BulletList":
                                default:
                                    s += "- " + Page_Validators[i].errormessage;
                                    if (i < lastValIndex) {
                                        s += "\r\n";
                                    }
                                    break;
                            }
                        }
                    }
                    $alert(s, function() {
                        // 在消息窗口关闭之后重定位焦点
                        for (i = 0; i <= lastValIndex; i++) {
                            var val = Page_Validators[i];
                            if (!val.isvalid && Page_InvalidControlToBeFocused == null &&
                            typeof (val.focusOnError) == "string" && val.focusOnError == "t") {
                                ValidatorSetFocus(val, null);
                            }
                        }
                    });
                }
            }
        }
    }
}
_Page.registerClass('_Page');
Page = new _Page();

Page._createQueryString = function(queryString) {
    var sb = new Sys.StringBuilder();
    var i = 0;
    for (var arg in queryString) {
        var obj = queryString[arg];
        if (typeof (obj) === "function") continue;
        var val = obj ? obj.toString() : '';
        if (i !== 0) {
            sb.append('&');
        }
        sb.append(arg);
        sb.append('=');
        sb.append(encodeURIComponent(val));
        i++;
    }
    return sb.toString();
}

Page.createUrl = function(url, queryString) {
    if (!queryString) {
        return url;
    }

    var qs = Page._createQueryString(queryString);
    if (qs.length > 0) {
        var sep = '?';
        if (url && url.indexOf('?') !== -1)
            sep = '&';
        return url + sep + qs;
    } else {
        return url;
    }
}

Page.NodeType = function() {
    /// <summary>
    /// Dom节点的类型。
    /// </summary>
    /// <field name="element" type="Number" integer="true" static="true" />
    /// <field name="attribute" type="Number" integer="true" static="true" />
    /// <field name="text" type="Number" integer="true" static="true" />
    /// <field name="cdata" type="Number" integer="true" static="true" />
    /// <field name="entityReference" type="Number" integer="true" static="true" />
    /// <field name="pi" type="Number" integer="true" static="true" />
    /// <field name="comment" type="Number" integer="true" static="true" />
    /// <field name="document" type="Number" integer="true" static="true" />
    /// <field name="docmentType" type="Number" integer="true" static="true" />
    /// <field name="documentFragment" type="Number" integer="true" static="true" />
    /// <field name="notation" type="Number" integer="true" static="true" />
    if (arguments.length !== 0) throw Error.parameterCount();
    throw Error.notImplemented();
}

Page.NodeType.prototype = {
    element: 1,
    attribute: 2,
    text: 3,
    cdata: 4,
    entityReference: 5,
    entity: 6,
    pi: 7,
    comment: 8,
    document: 9,
    docmentType: 10,
    documentFragment: 11,
    notation: 12
}
Page.NodeType.registerEnum("Page.NodeType");

Page.Cursor = function() {
    /// <summary>
    /// 页面元素的鼠标样式。
    /// </summary>
    /// <field name="Default" type="Number" integer="true" static="true" />
    /// <field name="Pointer" type="Number" integer="true" static="true" />
    /// <field name="Text" type="Number" integer="true" static="true" />
    /// <field name="Progress" type="Number" integer="true" static="true" />
    /// <field name="Move" type="Number" integer="true" static="true" />
    /// <field name="Help" type="Number" integer="true" static="true" />
    /// <field name="CrossHair" type="Number" integer="true" static="true" />
    /// <field name="Wait" type="Number" integer="true" static="true" />
    /// <field name="NResize" type="Number" integer="true" static="true" />
    /// <field name="NWResize" type="Number" integer="true" static="true" />
    /// <field name="WResize" type="Number" integer="true" static="true" />
    /// <field name="SWResize" type="Number" integer="true" static="true" />
    /// <field name="SResize" type="Number" integer="true" static="true" />
    /// <field name="SEResize" type="Number" integer="true" static="true" />
    /// <field name="EResize" type="Number" integer="true" static="true" />
    /// <field name="NEResize" type="Number" integer="true" static="true" />
    /// <field name="NotAllowed" type="Number" integer="true" static="true" />
    /// <field name="ColResize" type="Number" integer="true" static="true" />
    /// <field name="RowResize" type="Number" integer="true" static="true" />
    /// <field name="VerticalText" type="Number" integer="true" static="true" />
    if (arguments.length !== 0) throw Error.parameterCount();
    throw Error.notImplemented();
}

Page.Cursor.prototype = {
    /// <summary>
    /// default
    /// </summary>
    Default: 0,
    /// <summary>
    /// pointer
    /// </summary>
    Pointer: 1,
    /// <summary>
    /// text
    /// </summary>
    Text: 2,
    /// <summary>
    /// progress
    /// </summary>
    Progress: 3,
    /// <summary>
    /// move
    /// </summary>
    Move: 4,
    /// <summary>
    /// help
    /// </summary>
    /// <remarks>
    /// The arrow of the help cursor is a bit smaller than
    /// the one in other cursors on Windows
    /// </remarks>
    Help: 5,
    /// <summary>
    /// cross-hair
    /// </summary>
    CrossHair: 6,
    /// <summary>
    /// wait
    /// </summary>
    Wait: 7,
    /// <summary>
    /// n-resize
    /// </summary>
    NResize: 8,
    /// <summary>
    /// nw-resize
    /// </summary>
    NWResize: 9,
    /// <summary>
    /// w-resize
    /// </summary>
    WResize: 10,
    /// <summary>
    /// sw-resize
    /// </summary>
    SWResize: 11,
    /// <summary>
    /// s-resize
    /// </summary>
    SResize: 12,
    /// <summary>
    /// se-resize
    /// </summary>
    SEResize: 13,
    /// <summary>
    /// ne-resize
    /// </summary>
    EResize: 14,
    /// <summary>
    /// ne-resize
    /// </summary>
    NEResize: 15,
    /// <summary>
    /// not-allowed
    /// </summary>
    /// <remarks>
    /// Not supported by opera and Konqueror
    /// </remarks>
    NotAllowed: 16,
    /// <summary>
    /// col-resize
    /// </summary>
    /// <remarks>
    /// Not supported by opera and Konqueror
    /// </remarks>
    ColResize: 17,
    /// <summary>
    /// col-resize
    /// </summary>
    /// <remarks>
    /// Not supported by opera and Konqueror
    /// </remarks>
    RowResize: 18,
    /// <summary>
    /// col-resize
    /// </summary>
    /// <remarks>
    /// Not supported by opera and Konqueror
    /// </remarks>
    VerticalText: 19
}
Page.Cursor.registerEnum("Page.Cursor");

Page.Drag = function() {
    /// <summary>
    /// 鼠标拖动效果的基类。可以通过对此类的继承实现页面元素的拖动和改变大小等效果。
    /// </summary>

    // Target Sys.UI.DomElement of the drag
    this._target = null;
    // Handle Sys.UI.DomElement of the drag
    this._dragHandle = null;

    // Mouse location last move ation executed
    this._mouseLocation = null;
    // Mouse location when drag started
    this._startMouseLocation = null;
    // Target bounds when drag started
    this._bounds = null;

    // Document mouseup event handler
    this._mouseupHandler = null;
    // Document mousemove event handler
    this._mousemoveHandler = null;
    // Drag handle mousedown event handler
    this._mousedownHandler = null;
    // Timer tick event handler
    this._initialized = false;
    this._isMoving = false;
}

Page.Drag.prototype = {
    initialize: function() {
        this._mousedownHandler = Function.createDelegate(this, this._onMouseDown);
        Array.forEach(this.get_handles(), function(elt, index, context) {
            $addHandler(elt, 'mousedown', context);
        }, this._mousedownHandler);
        this._initialized = true;
    },
    dispose: function() {
        if (this._mousedownHandler) {
            Array.forEach(this.get_handles(), function(elt, index, context) {
                $removeHandler(elt, 'mousedown', context);
            }, this._mousedownHandler);
            this._mousedownHandler = null;
        }
        this._mouseupHandler = null;
        delete this._events;
    },
    _onMouseDown: function(evt) {
        if (evt.button == Sys.UI.MouseButton.leftButton && !evt.ctrlKey && !evt.shiftKey && !evt.altKey) {
            this._startMouseLocation = new Sys.UI.Point(evt.clientX, evt.clientY);
            this.start(evt.target);
            //阻止默认动作
            evt.preventDefault();
        }
    },
    start: function(target) {
        /// <summary>
        /// 开始拖动操作。
        /// </summary>
        /// <param name="target" domElement="true" mayBeNull="false">引发拖动操作的句柄对象。</param>
        /// <returns />
        if (!this._mouseupHandler) {
            this._mouseupHandler = Function.createDelegate(this, this._onMouseup);
        }
        $addHandler(document, "mouseup", this._mouseupHandler);
        if (!this._mousemoveHandler) {
            this._mousemoveHandler = Function.createDelegate(this, this._onMouseMove);
        }
        $addHandler(document, "mousemove", this._mousemoveHandler);
        this._dragHandle = target;
        // 在IE中要锁定鼠标事件，避免鼠标拖动到iframe时mousemove事件丢失。
        if (Sys.Browser.agent == Sys.Browser.InternetExplorer) {
            this._dragHandle.setCapture();
        }
        // 在某些浏览器中当Mouse移到Iframe中时，该Iframe将会拦截该事件。
        // 所以在拖动的过程中增加一个透明的遮罩层DIV元素，
        // 当要进行拖动时，该层会显示，当拖动完成后，该层会隐藏。
        // 同时也可以方便在拖动的过程中设置不同的鼠标样式。
        Page.Drag.showMask();
        // 设置鼠标指针的样式与拖动的对象相同
        Page.Drag.setCursor($common.getCurrentStyle(target, 'cursor'));
        // 记录被目标对象的起始位置
        var parentElement = Page.getPositionParent(this._target);
        var location = $common.getLocation(this._target);
        var parentLocation = $common.getLocation(Page.getPositionParent(this._target));
        this._bounds = new Sys.UI.Bounds(location.x - parentLocation.x, location.y - parentLocation.y, this._target.clientWidth, this._target.clientHeight);
        this._isMoving = true;
        this.raiseStarted(Sys.EventArgs.Empty);
    },
    _onMouseup: function(evt) {
        if (evt.button == Sys.UI.MouseButton.leftButton) {
            this.stop();
        }
    },
    stop: function() {
        /// <summary>
        /// 停止拖动。
        /// </summary>
        /// <returns />
        this._isMoving = false;
        //移除事件
        $removeHandler(document, "mouseup", this._mouseupHandler);
        $removeHandler(document, "mousemove", this._mousemoveHandler);
        // 隐藏遮罩层
        Page.Drag.hideMask();
        if (Sys.Browser.agent == Sys.Browser.InternetExplorer) {
            this._dragHandle.releaseCapture();
        }
        this._dragHandle = null;
        this._bounds = null;
        this._startMouseLocation = null;
        this._mouseLocation = null;
        this.raiseStopped(Sys.EventArgs.Empty);
    },
    _onMouseMove: function(evt) {
        // 如果被锁定则直接停止
        if (this._locked) {
            this.stop();
            return;
        }
        var mouseLocation = new Sys.UI.Point(evt.clientX, evt.clientY);
        // 如果鼠标相对于上一次响应时的位置未发生变化的情况下不进行任何处理，
        // 从而降低移动过程中的运算量。
        if (this._mouseLocation && this._mouseLocation.x == mouseLocation.x && this._mouseLocation.y == mouseLocation.y) {
            return;
        }
        this._mouseLocation = mouseLocation;
        // 清除选择
        window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
        this.move(mouseLocation);
    },
    move: function(location) {
        /// <summary>
        /// 在拖动的过程中执行鼠标移动时的动作。
        /// </summary>
        /// <returns />
        throw Error.notImplemented();
    },
    get_target: function() {
        /// <value domElement="true" mayBeNull="false">
        /// 被拖动的目标。
        /// </value>
        return this._target;
    },
    set_target: function(value) {
        /// <value domElement="true" mayBeNull="false">
        /// 被拖动的目标。
        /// </value>
        this._target = value;
    },
    get_handles: function() {
        /// <summary>
        /// 获取所有的拖动句柄对象。
        /// </summary>
        throw Error.notImplemented();
    },
    set_handles: function(value) {
        var handles = this.get_handles();
        if (this.get_isInitialized()) {
            if (handles) {
                Array.forEach(handles, function(elt, index, context) {
                    $removeHandler(elt, 'mousedown', context);
                }, this._mousedownHandler);
            }
            if (value) {
                Array.forEach(value, function(elt, index, context) {
                    $addHandler(elt, 'mousedown', context);
                }, this._mousedownHandler);
            }
        }
    },
    get_locked: function() {
        /// <value type="Boolean">
        /// <code>true</code> 目标元素被锁定，<code>false</code> 未锁定。
        /// </value>
        return this._locked;
    },
    set_locked: function(value) {
        /// <value type="Boolean">
        /// <code>true</code> 目标元素被锁定，<code>false</code> 未锁定。
        /// </value>
        this._locked = value;
    },
    get_isMoving: function() {
        /// <value type="Boolean">
        /// <code>true</code> 拖动在进行中， <code>false</code> 未拖动。
        /// </value>
        return this._isMoving;
    },
    get_startBounds: function() {
        /// <value type="Sys.UI.Bounds">
        /// 拖动开始时目标元素的区域。
        /// </value>
        return this._bounds;
    },
    get_startMouseLocation: function() {
        /// <value type="Sys.UI.Point">
        /// 拖动开始时鼠标的位置。
        /// </value>
        return this._startMouseLocation;
    },
    get_isInitialized: function() {
        return this._initialized;
    },
    get_events: function() {
        /// <returns type="Sys.EventHandlerList"></returns>
        if (!this._events) {
            this._events = new Sys.EventHandlerList();
        }
        return this._events;
    },
    add_started: function(handler) {
        /// <summary>
        /// 为拖动开始的事件添加处理函数。
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// 处理函数的引用。
        /// </param>
        /// <returns />
        this.get_events().addHandler("started", handler);
    },
    remove_started: function(handler) {
        /// <summary>
        /// 移除拖动开始事件的处理函数。
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// 处理函数的引用。
        /// </param>
        /// <returns />
        this.get_events().removeHandler("started", handler);
    },
    raiseStarted: function(eventArgs) {
        /// <summary>
        /// 引发拖动开始事件。
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// 拖动开始事件的参数。
        /// </param>
        /// <returns />
        var handler = this.get_events().getHandler('started');
        if (handler) {
            handler(this, eventArgs);
        }
    },
    add_stopped: function(handler) {
        /// <summary>
        /// 为拖动结束的事件添加处理函数。
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// 处理函数的引用。
        /// </param>
        /// <returns />
        this.get_events().addHandler("stopped", handler);
    },
    remove_stopped: function(handler) {
        /// <summary>
        /// 移除拖动结束事件的处理函数。
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// 处理函数的引用。
        /// </param>
        /// <returns />
        this.get_events().removeHandler("stopped", handler);
    },
    raiseStopped: function(eventArgs) {
        /// <summary>
        /// 引发拖动结束事件。
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// 拖动结束事件的参数。
        /// </param>
        /// <returns />
        var handler = this.get_events().getHandler('stopped');
        if (handler) {
            handler(this, eventArgs);
        }
    },
    add_move: function(handler) {
        /// <summary>
        /// 为拖动移动的事件添加处理函数。
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// 处理函数的引用。
        /// </param>
        /// <returns />
        this.get_events().addHandler("move", handler);
    },
    remove_move: function(handler) {
        /// <summary>
        /// 移除拖动移动事件的处理函数。
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// 处理函数的引用。
        /// </param>
        /// <returns />
        this.get_events().removeHandler("move", handler);
    },
    raiseMove: function(eventArgs) {
        /// <summary>
        /// 引发拖动移动事件。
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// 拖动移动事件的参数。
        /// </param>
        /// <returns />
        var handler = this.get_events().getHandler('move');
        if (handler) {
            handler(this, eventArgs);
        }
    }
}

Page.Drag.registerClass('Page.Drag');

// 用以捕获鼠标事件的遮罩层。
// 使用静态的遮罩层来避免重复的创建和移除页面元素。
Page.Drag._maskElement = null;
Page.Drag.showMask = function() {
    /// <summary>
    /// 显示遮罩层，在被拖动的对象中包含Iframe时避免鼠标事件丢失。
    /// </summary>
    if (!Page.Drag._maskElement) {
        Page.Drag._maskElement = $common.createElementFromTemplate({
            nodeName: 'DIV',
            properties: {
                style: {
                    position: 'absolute',
                    left: '0px',
                    top: '0px'
                }
            }
        }, document.body);
        // 在IE中必须设置背景颜色之后才可以使DIV遮挡下面的元素
        if (Sys.Browser.agent == Sys.Browser.InternetExplorer && Sys.Browser.version >= 7) {
            $common.setStyle(Page.Drag._maskElement, {
                backgroundColor: '#000'
            });
            $common.setElementOpacity(Page.Drag._maskElement, 0.0);
        }
    }
    // 注册鼠标事件，在窗口大小或滚动位置发生变化时遮罩层会随之变化。
    $addHandler(window, 'resize', Page.Drag.layoutMaskElement);
    $addHandler(window, 'scroll', Page.Drag.layoutMaskElement);
    $addHandler(window, 'mousewheel', Page.Drag.layoutMaskElement);
    $common.setVisible(Page.Drag._maskElement, true);
    $common.setStyle(Page.Drag._maskElement, { zIndex: Page.getMaxLevel(null, [Page.Drag._maskElement]) + 1 }); // 使蒙板位于页面的最顶层
    Page.Drag.layoutMaskElement();
}
Page.Drag.layoutMaskElement = function() {
    /// <summary>
    /// 改变遮罩层的大小使其覆盖整个窗口。
    /// </summary>
    var clientBounds = $common.getClientBounds();
    var clientWidth = clientBounds.width;
    var clientHeight = clientBounds.height;
    Page.Drag._maskElement.style.width = Math.max(Math.max(document.documentElement.scrollWidth, document.body.scrollWidth), clientWidth) + 'px';
    Page.Drag._maskElement.style.height = Math.max(Math.max(document.documentElement.scrollHeight, document.body.scrollHeight), clientHeight) + 'px';
}
Page.Drag.hideMask = function() {
    /// <summary>
    /// 隐藏遮罩层。
    /// </summary>
    $common.setVisible(Page.Drag._maskElement, false);
}

Page.Drag.setCursor = function(cursor) {
    /// <summary>
    /// 在拖动的过程中设置鼠标的样式。
    /// </summary>
    /// <param name="cursor" mayBeNull="false">
    /// 鼠标的样式。字符串或者 Page.Cursor。
    /// </param>
    var type = Object.getType(cursor);
    if (type == String) {
        Page.Drag._maskElement.style.cursor = cursor;
    } else if (type == Number) {
        Page.setCursor(Page.Drag._maskElement, cursor);
    }
}


Page.Move = function() {
    /// <summary>
    /// 鼠标拖动页面元素移动的效果。
    /// </summary>
    Page.Move.initializeBase(this);
    this._handles = [];
    this._limit = {};
    this._cursor = Page.Cursor.Move;
}

Page.Move.prototype = {
    initialize: function() {
        Page.Move.callBaseMethod(this, 'initialize');
        this._layoutCursor();
    },
    dispose: function() {
        Page.Move.callBaseMethod(this, 'dispose');
    },
    _layoutCursor: function() {
        Array.forEach(this._handles, function(elt) {
            Page.setCursor(elt, this._cursor);
        });
    },
    move: function(location) {
        /// <summary>
        /// 在拖动的过程中改变目标元素的大小和位置。
        /// </summary>
        /// <param name="location" type="Sys.UI.Point" mayBeNull="false">
        /// 移动过程中的鼠标位置。
        /// </param>
        /// <returns />
        if (!this.get_isMoving()) return false;
        var bounds = this.get_startBounds();
        var mouseLocation = this.get_startMouseLocation();
        var left = location.x - mouseLocation.x + bounds.x;
        var top = location.y - mouseLocation.y + bounds.y;
        var target = this.get_target();
        // 修正移动参数
        var maxRight = this._limit.right;
        if (maxRight != undefined && maxRight != null) {
            left = Math.min(left, maxRight - target.offsetWidth);
        }
        var minLeft = this._limit.left;
        if (minLeft != undefined && minLeft != null) {
            left = Math.max(left, minLeft);
        }
        var maxBottom = this._limit.bottom;
        if (maxBottom != undefined && maxBottom != null) {
            top = Math.min(top, maxBottom - target.offsetHeight);
        }
        var minTop = this._limit.top;
        if (minTop != undefined && minTop != null) {
            top = Math.max(top, minTop);
        }
        var style = target.style;
        style.position = 'absolute';
        style.left = left + "px";
        style.top = top + "px";
        this.raiseMove(new Sys.UI.Point(left - bounds.x, top - bounds.y));
    },
    get_handles: function() {
        /// <summary>
        /// 获取所有的拖动句柄对象。
        /// </summary>
        return this._handles;
    },
    set_handles: function(value) {
        Page.Move.callBaseMethod(this, 'set_handles', [value]);
        this._handles = value;
        this._layoutCursor();
    },
    get_limit: function() {
        /// <summary>
        /// 获取移动的范围限制。
        /// </summary>
        return this._limit;
    },
    get_cursor: function() {
        return this._cursor;
    },
    set_cursor: function(value) {
        this._cursor = value;
    }
}

Page.Move.registerClass('Page.Move', Page.Drag);


Page.Direction = function() {
    /// <field name="Left" type="Number" integer="true" static="true" />
    /// <field name="TopLeft" type="Number" integer="true" static="true" />
    /// <field name="Top" type="Number" integer="true" static="true" />
    /// <field name="TopRight" type="Number" integer="true" static="true" />
    /// <field name="Right" type="Number" integer="true" static="true" />
    /// <field name="BottomRight" type="Number" integer="true" static="true" />
    /// <field name="Bottom" type="Number" integer="true" static="true" />
    /// <field name="BottomLeft" type="Number" integer="true" static="true" />
    if (arguments.length !== 0) throw Error.parameterCount();
    throw Error.notImplemented();
}

Page.Direction.prototype = {
    Left: 0x01,
    TopLeft: 0x03,
    Top: 0x02,
    TopRight: 0x06,
    Right: 0x04,
    BottomRight: 0x14,
    Bottom: 0x10,
    BottomLeft: 0x11
}
Page.Direction.registerEnum("Page.Direction");

Page.Resize = function() {
    Page.Resize.initializeBase(this);
    this._direction = null;
    this._maxSize = {};
    this._minSize = {};
    this._handles = null;
}

Page.Resize.prototype = {
    initialize: function() {
        if (!this._handles) {
            this._handles = this._searchHandlers(this._target);
        }
        Page.Resize.callBaseMethod(this, 'initialize');
        this._layoutCursor();
    },
    dispose: function() {
        Page.Resize.callBaseMethod(this, 'dispose');
    },
    _searchHandlers: function(parentElement) {
        var r = [];
        Array.forEach($array(parentElement.childNodes), childrenIterator, r);
        return r;
        function childrenIterator(elt, index, context) {
            if (elt.nodeType == 1) {
                if (elt.getAttribute('resizeDirection') && !Array.contains(context, elt)) Array.add(context, elt);
                Array.forEach($array(elt.childNodes), childrenIterator, context);
            }
        }
    },
    _layoutCursor: function() {
        if (this._handles) {
            // 为每一个包含resizeDirection属性的元素设置鼠标样式
            Array.forEach(this._handles, function(elt) {
                // 设置鼠标的样式。考虑到兼容性，不能使用自定义的鼠标样式（只有IE支持此功能）。
                var direction = elt.getAttribute('resizeDirection');
                var cursor = '';
                if (direction.startsWith('Bottom')) cursor += 's';
                if (direction.startsWith('Top')) cursor += 'n';
                if (direction.endsWith('Left')) cursor += 'w';
                if (direction.endsWith('Right')) cursor += 'e';
                elt.style.cursor = cursor + '-resize';
            });
        }
    },
    start: function(target) {
        Page.Resize.callBaseMethod(this, 'start', [target]);
        this._direction = Page.Direction.parse(target.getAttribute('resizeDirection'), true);
    },
    stop: function() {
        Page.Resize.callBaseMethod(this, 'stop');
        this._direction = null;
    },
    move: function(location) {
        if (!this.get_isMoving()) return false;
        // 获取移动之前的数值状态
        var target = this.get_target();
        var bounds = this.get_startBounds();
        var x = bounds.x, y = bounds.y, width = bounds.width, height = bounds.height;

        var startLocation = this.get_startMouseLocation();
        var moveLeft = (this._direction & Page.Direction.Left) == Page.Direction.Left;
        var moveRight = (this._direction & Page.Direction.Right) == Page.Direction.Right;
        var moveTop = (this._direction & Page.Direction.Top) == Page.Direction.Top;
        var moveBottom = (this._direction & Page.Direction.Bottom) == Page.Direction.Bottom;
        if (moveLeft || moveRight) {
            width -= (location.x - startLocation.x) * (moveLeft ? 1 : -1);
            // 根据限制调整宽度的值
            if (this._maxSize && this._maxSize.width != undefined && this._maxSize.width != null) {
                width = Math.min(this._maxSize.width, width);
            }
            if (this._minSize && this._minSize.width != undefined && this._minSize.width != null) {
                width = Math.max(this._minSize.width, width);
            }
        }
        if (moveTop || moveBottom) {
            height = bounds.height - (location.y - startLocation.y) * (moveTop ? 1 : -1);
            // 根据限制调整高度的值
            if (this._maxSize && this._maxSize.height != undefined && this._maxSize.height != null) {
                height = Math.min(this._maxSize.height, height);
            }
            if (this._minSize && this._minSize.height != undefined && this._minSize.height != null) {
                height = Math.max(this._minSize.height, height);
            }
        }
        // 限制最小值不能使$common.setSize出现错误
        var borderBox = $common.getBorderBox(target);
        var paddingBox = $common.getPaddingBox(target);
        width = Math.max(width, 1 + borderBox.horizontal + paddingBox.horizontal);
        height = Math.max(height, 1 + borderBox.vertical + paddingBox.vertical);
        // 对宽度进行调整后，再对横坐标进行调整，
        // 以保证在拖动左边的resizer的时候其他部分不会发生任何移动
        if (moveLeft) {
            x += bounds.width - width;
        }
        // 对高度调整之后再对纵坐标进行调整
        // 以保证在拖动上面的resizer的时候其他部分不会发生移动
        if (moveTop) {
            y += bounds.height - height;
        }
        $common.setBounds(target, new Sys.UI.Bounds(x, y, width, height));
        this.raiseMove(new Sys.UI.Bounds(x - bounds.x, y - bounds.y, width - bounds.width, height - bounds.height));
    },
    get_handles: function() {
        /// <summary>
        /// 获取所有的拖动句柄对象。
        /// </summary>
        return this._handles;
    },
    set_handles: function(value) {
        Page.Resize.callBaseMethod(this, 'set_handles', [value]);
        this._handles = value;
        this._layoutCursor();
    },
    get_maxSize: function() {
        return this._maxSize;
    },
    get_minSize: function() {
        return this._minSize;
    }
}

Page.Resize.registerClass('Page.Resize', Page.Drag);


Page.Dialog = function() {
    this._initialized = false;
    this._shown = false;
    this._window = null;
    this._backgroundElement = null;
    this._handler = null;
    this._buttonClose = null;
    this._bottomMiddle = null;
    this._leftBorder = null;
    this._rightBorder = null;
    this._contentElement = null;
    this._drag = null;
    this._resize = null;
    this._closeClickHandler = null;
    this._preventHandler = null;
    this._stopHandler = null;

    this._overflow = null;
    this._position = null;
    this._repositioning = false;
    this._saveTabIndexes = [];
    this._saveDisableSelect = [];
    this._tagWithTabIndex = ['A', 'AREA', 'BUTTON', 'INPUT', 'OBJECT', 'SELECT', 'TEXTAREA', 'IFRAME'];
}

Page.Dialog.prototype = {
    initialize: function() {
    },
    dispose: function() {
        this.hide();
        if (this._resize) {
            this._resize.dispose();
            this._resize = null;
        }
        if (this._drag) {
            this._drag.dispose();
            this._drag = null;
        }
        if (this._stopHandler) {
            $removeHandler(this._buttonClose, 'mousedown', this._stopHandler);
            this._stopHandler = null;
        }
        if (this._closeClickHandler) {
            $removeHandler(this._buttonClose, 'click', this._closeClickHandler);
            this._closeClickHandler = null;
        }
        if (this._window) {
            $common.removeElement(this._window);
            this._window = null;
        }
        if (this._backgroundElement) {
            $common.removeElement(this._backgroundElement);
            this._backgroundElement = null;
        }
        if (this._layoutHandler) {
            $removeHandler(window, 'load', this._layoutHandler);
            this._layoutHandler = null;
        }
    },
    get_isInitialized: function() {
        return this._initialized;
    },
    get_isShown: function() {
        return this._shown;
    },
    _create: function() {
        // 创建窗口的容器
        this._window = $common.createElementFromTemplate({
            nodeName: 'DIV',
            properties: {
                className: 'Dialog',
                style: {
                    position: 'absolute',
                    borderWidth: '0px',
                    display: 'none',
                    zIndex: 1001
                },
                visible: false
            }
        }, document.body);
        // 创建蒙板元素，用于在显示消息的时候覆盖页面
        this._backgroundElement = $common.createElementFromTemplate({
            nodeName: 'DIV',
            properties: {
                id: 'messageMask',
                style: {
                    position: 'absolute',
                    left: '0px',
                    top: '0px'
                }
            },
            visible: false
        }, Page._form);
        // create title handler to support mouse drag action
        this._handler = $common.createElementFromTemplate({
            nodeName: 'DIV',
            properties: {
                className: 'Handler',
                style: {
                    borderWidth: '0px',
                    overflow: 'hidden'
                }
            }
        }, this._window);
        // create element to show the title text
        this._title = $common.createElementFromTemplate({
            nodeName: 'DIV',
            properties: {
                className: 'Title',
                style: {
                    borderWidth: '0px'
                }
            }
        }, $common.createElementFromTemplate({
            nodeName: 'DIV',
            properties: { className: 'TitleContainer' }
        }, this._handler));
        // create close button on the top right corner.
        this._buttonClose = $common.createElementFromTemplate({
            nodeName: 'INPUT',
            properties: {
                type: 'submit',
                value: '',
                className: 'CloseButton'
            }
        }, $common.createElementFromTemplate({
            nodeName: 'DIV',
            properties: {
                className: 'TitleRight',
                style: {
                    borderWidth: '0px'
                }
            }
        }, this._handler)
        );
        // create row for container
        var body = $common.createElementFromTemplate({
            nodeName: 'TBODY'
        }, $common.createElementFromTemplate({
            nodeName: 'Table',
            properties: {
                border: '0px',
                cellSpacing: '0px',
                cellPadding: '0px',
                className: 'ContentTable',
                style: {
                    borderWidth: '0px'
                }
            }
        }, this._window)
        );
        // create content row
        var contentRow = $common.createElementFromTemplate({ nodeName: 'TR' }, body);

        // left border for content row
        this._leftBorder = $common.createElementFromTemplate({
            nodeName: 'TD', properties: {
                className: 'ContentLeft'
            }
        }, contentRow);
        this._leftBorder.setAttribute('resizeDirection', Page.Direction.toString(Page.Direction.Left));
        this._contentElement = this.createContent($common.createElementFromTemplate({
            nodeName: 'TD',
            properties: {
                className: 'ContentMiddle'
            }
        }, contentRow));
        this._rightBorder = $common.createElementFromTemplate({
            nodeName: 'TD',
            properties: {
                className: 'ContentRight'
            }
        }, contentRow);
        this._rightBorder.setAttribute('resizeDirection', Page.Direction.toString(Page.Direction.Right));
        // create bottom row
        var bottomRow = $common.createElementFromTemplate({
            nodeName: 'TR',
            properties: {
                className: 'BottomRow'
            }
        }, body);
        var element = $common.createElementFromTemplate({
            nodeName: 'TD',
            properties: {
                className: 'BottomLeft'
            }
        }, bottomRow);
        element.setAttribute('resizeDirection', Page.Direction.toString(Page.Direction.BottomLeft));
        this._bottomMiddle = $common.createElementFromTemplate({
            nodeName: 'TD',
            properties: {
                className: 'BottomMiddle'
            }
        }, bottomRow);
        this._bottomMiddle.setAttribute('resizeDirection', Page.Direction.toString(Page.Direction.Bottom));
        element = $common.createElementFromTemplate({
            nodeName: 'TD',
            properties: {
                className: 'BottomRight'
            }
        }, bottomRow);
        element.setAttribute('resizeDirection', Page.Direction.toString(Page.Direction.BottomRight));
    },
    createContent: function(parent) {
        throw Error.notImplemented();
    },
    get_resizable: function() {
        throw Error.notImplemented();
    },
    setTitle: function(title) {
        // 显示标题
        $text(this._title, title);
    },
    show: function(title, position) {
        if (!this._initialized) {
            this._create();
            this._preventHandler = Function.createDelegate(this, this._preventEvent);
            this._stopHandler = Function.createDelegate(this, this._stopEvent);
            this._layoutHandler = Function.createDelegate(this, this._layout);
            var savePositionHandler = Function.createDelegate(this, this._savePosition);
            // 实现拖拽效果
            this._drag = $createObject(Page.Move, { target: this._window, handles: [this._handler] }, {
                stopped: savePositionHandler
            });
            this._closeClickHandler = Function.createDelegate(this, this._onClose);
            $addHandler(this._buttonClose, 'click', this._closeClickHandler);
            // 当关闭按钮点击时停止事件继续bubble，从而避免title点击时出现一个遮罩层而导致关闭按钮失效的问题(Firefox)
            $addHandler(this._buttonClose, 'mousedown', this._stopHandler);
            $addHandler(window, 'load', this._layoutHandler);
            if (this.get_resizable()) {
                // 实现拖动鼠标改变窗口大小
                this._resize = $createObject(Page.Resize, { target: this._window }, {
                    started: savePositionHandler,
                    move: savePositionHandler,
                    stopped: savePositionHandler
                });
            }
        }
        this.onShowing();
        this._initialized = true;
        if (this._shown) return;
        this.setTitle(title);
        this._shown = true;
        var zIndex = Page.getMaxLevel(null, [this._backgroundElement, this._window]);
        // 显示蒙板遮盖
        this._backgroundElement.style.zIndex = zIndex + 1;
        $common.setVisible(this._backgroundElement, true);
        this._window.style.zIndex = zIndex + 2;
        $common.setVisible(this._window, true);
        // 在弹出窗口时消息及任何内容不能被选择，并且禁止右键菜单
        $addHandler(document.body, 'selectstart', this._preventHandler);
        $addHandler(document.body, 'contextmenu', this._preventHandler);
        $addHandler(window, 'resize', this._layoutHandler);
        $addHandler(document.body, 'scroll', this._layoutHandler);

        this._position = position || 'center';

        this.onShown();
        // 内容显示完成之后，可能各个元素的大小会发生变化。因此需要重新设置窗口的大小和位置。
        var contentSize = $common.getSize(this._contentElement);
        $common.setSize(this._window, {
            width: contentSize.width + $common.getSize(this._leftBorder).width + $common.getSize(this._rightBorder).width,
            height: contentSize.height
            // 必须使用TD来确定底部的高度(不能使用TR)，因为在IE8的RC版中存在TR高度与TD高度不一致的问题。
            + $common.getSize(this._bottomMiddle).height
            + $common.getSize(this._handler).height
        });
        this._layout();
        // 如果还没有其他的窗口显示，则禁止掉页面上可输入元素的TabIndex
        this._disableTab();
        // 将当前的对象加入到全局缓存中。
        Array.add(Page.Dialog.__cache, this);
    },
    hide: function() {
        if (!this._shown) return;
        this.onHiding();
        Array.remove(Page.Dialog.__cache, this);
        $common.setVisible(this._window, false);
        $common.setVisible(this._backgroundElement, false);
        $removeHandler(document.body, 'selectstart', this._preventHandler);
        $removeHandler(document.body, 'contextmenu', this._preventHandler);
        $removeHandler(document.body, 'scroll', this._layoutHandler);
        $removeHandler(window, 'resize', this._layoutHandler);
        this._shown = false;
        if (this._window.style.removeAttribute) {
            this._window.style.removeAttribute("width");
            this._window.style.removeAttribute("height");
        } else {
            this._window.style.removeProperty("width");
            this._window.style.removeProperty("height");
        }
        this._restoreTab();
        this.onHidden();
    },
    onShowing: function() {
    },
    onShown: function() {
    },
    onHiding: function() {
    },
    onHidden: function() {
    },
    _layout: function() {
        if (this._repositioning || !this._shown) return;
        this._repositioning = true;
        this._layoutWindow();
        this._invalidateDragLimit();
        this._layoutBackgroundElement();
        this._repositioning = false;
    },
    _layoutBackgroundElement: function() {
        var clientBounds = $common.getClientBounds();
        var clientWidth = clientBounds.width;
        var clientHeight = clientBounds.height;
        this._backgroundElement.style.width = Math.max(Math.max(document.documentElement.scrollWidth, document.body.scrollWidth), clientWidth) + 'px';
        this._backgroundElement.style.height = Math.max(Math.max(document.documentElement.scrollHeight, document.body.scrollHeight), clientHeight) + 'px';
    },
    _invalidateDragLimit: function() {
        var bounds = Page.getScrollBounds();
        var windowSize = $common.getSize(this._window);
        var height = $common.getSize(this._title).height;
        this._drag.get_limit().top = bounds.y;
        this._drag.get_limit().bottom = bounds.y + bounds.height + windowSize.height - height;
        this._drag.get_limit().left = bounds.x - windowSize.width + height;
        this._drag.get_limit().right = bounds.x + bounds.width + windowSize.width - height;
    },
    _layoutWindow: function() {
        var bounds = Page.getScrollBounds();
        var windowSize = $common.getSize(this._window);
        var x = 0;
        var y = 0;
        if (typeof (this._position) === 'string' && this._position.toLowerCase() === 'center') {
            x = bounds.x + Math.max(0, Math.floor(bounds.width / 2.0 - windowSize.width / 2.0));
            y = bounds.y + Math.max(0, Math.floor(bounds.height / 2.0 - windowSize.height / 2.0));
        } else {
            if (typeof (this._position.x) === 'number') {
                x = bounds.x + this._position.x;
            } else if (typeof (this._position.x) === 'undefined' || this._position.x === null || (typeof (this._position.x) === 'string' && this._position.x.toLowerCase() === 'center')) {
                x = bounds.x + Math.max(0, Math.floor(bounds.width / 2.0 - windowSize.width / 2.0));
            }
            if (typeof (this._position.y) === 'number') {
                y = bounds.y + this._position.y;
            } else if (typeof (this._position.y) === 'undefined' || this._position.y === null || (typeof (this._position.y) === 'string' && this._position.y.toLowerCase() === 'middle')) {
                y = bounds.y + Math.max(0, Math.floor(bounds.height / 2.0 - windowSize.height / 2.0));
            }
        }
        $common.setLocation(this._window, new Sys.UI.Point(x, y));
    },
    _disableTab: function(parentElement) {
        /// <summary>
        /// Change the tab indices so we only tab through the modal popup
        /// (and hide SELECT tags in IE6)
        /// </summary>
        parentElement = parentElement || document;
        var _this = this;
        Array.forEach(this._tagWithTabIndex, function(tagName) {
            Array.forEach(document.getElementsByTagName(tagName), function(elt) {
                if (!AjaxControlToolkit.DomUtility.isDescendantOrSelf(_this._window, elt)) {
                    Array.add(_this._saveTabIndexes, { element: elt, index: elt.tabIndex });
                    elt.tabIndex = "-1";
                }
            });
        });

        //IE6 Bug with SELECT element always showing up on top
        if ((Sys.Browser.agent === Sys.Browser.InternetExplorer) && (Sys.Browser.version < 7)) {
            Array.forEach(parentElement.getElementsByTagName('SELECT'), function(elt) {
                if (!AjaxControlToolkit.DomUtility.isDescendantOrSelf(_this._window, elt)) {
                    Array.add(_this._saveDisableSelect, { element: elt, visib: $common.getCurrentStyle(elt, 'visibility') });
                    elt.style.visibility = 'hidden';
                }
            });
        }
        Array.forEach(parentElement.getElementsByTagName('IFRAME'), function(elt) {
            if (!AjaxControlToolkit.DomUtility.isDescendantOrSelf(_this._window, elt)) {
                _this._disableTab(elt.contentWindow.document);
            }
        });
    },
    _restoreTab: function() {
        /// <summary>
        /// Restore the tab indices so we tab through the page like normal
        /// (and restore SELECT tags in IE6)
        /// </summary>
        Array.forEach(this._saveTabIndexes, function(item) {
            item.element.tabIndex = item.index;
        });
        Array.clear(this._saveTabIndexes);
        //IE6 Bug with SELECT element always showing up on top
        if ((Sys.Browser.agent === Sys.Browser.InternetExplorer) && (Sys.Browser.version < 7)) {
            Array.forEach(this._saveDisableSelect, function(item) {
                item.element.style.visibility = item.visib;
            })
            Array.clear(this._saveDisableSelect);
        }
    },
    _onClose: function(evt) {
        evt.preventDefault();
        this.hide();
    },
    _savePosition: function() {
        var bounds = Page.getScrollBounds();
        var location = $common.getLocation(this._window);
        this._position = new Sys.UI.Point(location.x - bounds.x, location.y - bounds.y);
    },
    _preventEvent: function(evt) {
        evt.preventDefault();
    },
    _stopEvent: function(evt) {
        evt.stopPropagation();
    }
}
Page.Dialog.registerClass('Page.Dialog');

Page.Dialog.__cache = [];
Page.Dialog._overflow = null;
Page.Dialog._savedWidth = null;

Page.Dialog.find = function(predicate) {
    var foundItem = null;
    Array.forEach(Page.Dialog.__cache, function(item) {
        if (predicate(item)) {
            foundItem = item;
            throw $break;
        }
    });
    return foundItem;
}

Page.Message = function() {
    Page.Message.initializeBase(this);
    this._container = null;
    this._buttonContainer = null;
    this._contentDiv = null;
    this._textDiv = null;
    this._buttonOk = null;
    this._buttonCancel = null;
    this._context = null;
    this._queue = [];

    this._cancelClickHandler = null;
    this._okClickHandler = null;
}
Page.Message.prototype = {
    initialize: function() {
        Page.Message.callBaseMethod(this, 'initialize');
        $ready(Function.createDelegate(this, this._showQueue));
    },
    dispose: function() {
        if (window.top == window) {
            if (this._cancelClickHandler) {
                $removeHandler(this._buttonCancel, 'click', this._cancelClickHandler);
                this._cancelClickHandler = null;
            }
            if (this._okClickHandler) {
                $removeHandler(this._buttonOk, 'click', this._okClickHandler);
                this._okClickHandler = null;
            }
            Page.Message.callBaseMethod(this, 'dispose');
        }
    },
    get_resizable: function() {
        return false;
    },
    createContent: function(parent) {
        this._container = $common.createElementFromTemplate({
            nodeName: 'Div'
        }, parent);
        this._contentDiv = $common.createElementFromTemplate({
            nodeName: 'Div'
        }, this._container);
        this._textDiv = $common.createElementFromTemplate({
            nodeName: 'Div'
        }, this._contentDiv);
        this._buttonContainer = $common.createElementFromTemplate({
            nodeName: 'DIV',
            properties: {
                className: 'MessageButton'
            }
        }, this._container);
        this._buttonOk = $common.createElementFromTemplate({
            nodeName: 'INPUT',
            properties: {
                type: 'submit',
                value: 'OK',
                className: 'messageButton'
            }
        }, this._buttonContainer);
        this._buttonCancel = $common.createElementFromTemplate({
            nodeName: 'INPUT',
            properties: {
                type: 'submit',
                value: 'No',
                className: 'messageButton'
            }
        }, this._buttonContainer);
        return this._container;
    },
    onShowing: function() {
        if (!this.get_isInitialized()) {
            if (window.top == window) {
                this._cancelClickHandler = Function.createDelegate(this, this._onCancel);
                $addHandler(this._buttonCancel, 'click', this._cancelClickHandler);
                this._okClickHandler = Function.createDelegate(this, this._onOk);
                $addHandler(this._buttonOk, 'click', this._okClickHandler);
            }
        }
        // 显示消息内容
        $text(this._textDiv, typeof (this._context.message) === 'undefined' || this._context.message === null ? '' : this._context.message);
        // 设置消息内容的样式，样式名称与类型名称完全相同，可以在以后的改进过程中增加其他的样式从而支持新的消息类型
        this._contentDiv.className = this._context.type;
        if (Sys.Browser.agent == Sys.Browser.Firefox) {
            // 在firefox中使用minHeight和minWidth使DIV的高度和宽度随着内容的多少而变化，直接使用width和height的话则不会自动调整。
            $common.setStyle(this._contentDiv, { minWidth: '250px', minHeight: '50px' });
        } else {
            $common.setStyle(this._contentDiv, { width: '250px', height: '50px' });
        }
        // 只有在“确认”窗口中才需要显示取消按钮，其他情况只需要一个确定按钮即可
        $common.setVisible(this._buttonCancel, this._context.type == 'Confirm');
    },
    onShown: function() {
        // 当消息内容的行数过多时，消息的显示高度或宽度将会超过预设的大小
        // 因此需要在设置了消息内容和默认的消息显示大小之后，重新调整显示高度和宽度
        $common.setSize(this._contentDiv, {
            width: Math.max(this._contentDiv.scrollWidth, this._contentDiv.offsetWidth),
            height: Math.max(this._contentDiv.offsetHeight, this._contentDiv.scrollHeight)
        });
        this._layout();
        var clientBounds = $common.getClientBounds();
        // 显示消息之后需要重新调整消息容器的大小，以便在显示完成之后调整窗口大小时的提供依据。
        var maxWidth = Math.max(Math.floor(clientBounds.width / 2), 500);
        var maxHeight = Math.max(Math.floor(clientBounds.height / 2), 400);
        // 消息窗口的高度和宽度都不能超过窗口可显示内容大小的一半。
        if (this._textDiv.offsetWidth > maxWidth || this._textDiv.offsetHeight > maxHeight) {
            var size = {
                width: Math.min(maxWidth, this._textDiv.offsetWidth),
                height: Math.min(maxHeight, this._textDiv.offsetHeight)
            }
            $common.setSize(this._textDiv, size);
            this._textDiv.style.overflow = 'auto';
            $common.setContentSize(this._contentDiv, size);
        }
        $common.setSize(this._container, {
            width: this._contentDiv.offsetWidth,
            height: this._contentDiv.offsetHeight + this._buttonContainer.offsetHeight
        });
        // 在消息窗口显示的时候直接使确定按钮获得焦点，以方便使用Enter键直接关闭消息
        this._buttonOk.focus();
    },
    onHidden: function() {
        if (this._context.callback) {
            if (this._context.type == 'Confirm') {
                this._context.callback(this._context.result);
            } else {
                this._context.callback();
            }
        }
        // 在窗口关闭之后清除掉width和height，避免下一次打开窗口的时候沿用上一次的设置。
        this._container.style.width = '';
        this._container.style.height = '';
        this._contentDiv.style.width = '';
        this._contentDiv.style.height = '';
        this._textDiv.style.width = '';
        this._textDiv.style.height = '';
        this._textDiv.style.overflow = '';
        this._context = null;
        this._position = null;
        this._showQueue();
    },
    show: function(type, message, callback, title) {
        if (window.top == window) {
            // 如果页面尚未加载或者有消息正在显示，则将当前的消息放入队列中
            // 等待页面加载完成之后或者正在显示的消息窗口关闭之后显示队列中的消息
            var context = { type: type, message: message, title: title, callback: callback, result: false };
            if (this.get_isShown() || !Page.get_isReady()) {
                Array.add(this._queue, context);
                return;
            }
            this._context = context;
            Page.Message.callBaseMethod(this, 'show', [title]);
        } else {
            window.top._messageObj.show(type, message, callback, title);
        }
    },
    _showQueue: function() {
        var next = Array.dequeue(this._queue);
        if (next) {
            this.show(next.type, next.message, next.callback, next.title);
        }
    },
    _onCancel: function(evt) {
        evt.preventDefault();
        this._context.result = false;
        this.hide();
    },
    _onOk: function(evt) {
        evt.preventDefault();
        this._context.result = this._context.type == 'Confirm';
        this.hide();
    }
}
Page.Message.registerClass('Page.Message', Page.Dialog);

Page.ModalDialog = function() {
    Page.ModalDialog.initializeBase(this);
    this._contentFrame = null;
    this._context = null;
    this._closed = false;
    this._loaded = false;

    this._frameLoadedHandler = null;
    this._frameSize = null;
}

Page.ModalDialog.prototype = {
    initialize: function() {
        Page.ModalDialog.callBaseMethod(this, 'initialize');
        this._frameLoadedHandler = Function.createDelegate(this, this._onFrameLoaded);
    },
    dispose: function() {
        this._frameLoadedHandler = null;
        Page.ModalDialog.callBaseMethod(this, 'dispose');
    },
    get_resizable: function() {
        return true;
    },
    createContent: function(parent) {
        return this._contentFrame = $common.createElementFromTemplate({
            nodeName: 'IFRAME',
            properties: {
                frameBorder: '0px',
                marginHeight: '0px',
                marginWidth: '0px',
                src: 'about:blank',
                style: {
                    borderWidth: '0px'
                }
            }
        }, parent);
    },
    show: function(url, bounds, callback, dialogArguments) {
        this._context = { url: url, callback: callback, bounds: bounds, dialogArguments: dialogArguments };
        Page.ModalDialog.callBaseMethod(this, 'show', [null, bounds ? { x: bounds.x || bounds.left, y: bounds.y || bounds.top} : null]);
    },
    onShowing: function() {
        if (this._context.bounds) {
            $common.setSize(this._contentFrame, { width: this._context.bounds.width, height: this._context.bounds.height });
        }
        //        if (this._context.bounds && typeof (this._context.bounds.scrollBar) != 'undefined') {
        //            if (typeof (this._context.bounds.scrollBar) == 'string') {
        //                this._contentFrame.scrolling = this._context.bounds.scrollBar;
        //            } else if (typeof (this._context.bounds.scrollBar) == 'boolean') {
        //                this._contentFrame.scrolling = this._context.bounds.scrollBar ? 'yes' : 'no';
        //            } else {
        //                this._contentFrame.scrolling = 'auto';
        //            }
        //        } else {
        //            this._contentFrame.scrolling = 'auto';
        //        }
        this._contentFrame.scrolling = 'no';
        if (this.get_resizable()) {
            this._resize.add_started(Function.createDelegate(this, this._onResizeStarted));
            this._resize.add_stopped(Function.createDelegate(this, this._onResizeEnded));
            this._resize.add_move(Function.createDelegate(this, this._onResizing));
        }
    },
    onShown: function() {
        this._contentFrame.src = this._context.url;
        $addHandler(this._contentFrame, 'load', this._frameLoadedHandler);
    },
    onHiding: function() {
        if (!this._loaded) {
            $removeHandler(this._contentFrame, 'load', this._frameLoadedHandler);
        }
    },
    onHidden: function() {
        var result = this._contentFrame.contentWindow.returnValue;
        this._contentFrame.contentWindow.returnValue = null;
        this._contentFrame.src = 'about:blank';
        if (this._context.callback) {
            this._context.callback(result);
        }
        this._context = null;
        this._closed = true;
        if (this.get_resizable()) {
            this._resize.remove_started(Function.createDelegate(this, this._onResizeStarted));
            this._resize.remove_stopped(Function.createDelegate(this, this._onResizeEnded));
            this._resize.remove_move(Function.createDelegate(this, this._onResizing));
        }
    },
    _onFrameLoaded: function() {
        if (this._closed) return;

        this.setTitle(this._contentFrame.contentWindow.document.title);
        $removeHandler(this._contentFrame, 'load', this._frameLoadedHandler);
        this._contentFrame.contentWindow.dialogArguments = this._context.dialogArguments;
        this._contentFrame.focus();
        if (typeof (this._contentFrame.contentWindow.pageInit) === 'function') {
            this._contentFrame.contentWindow.pageInit();
        }
        this._loaded = true;
    },
    _onResizeStarted: function() {
        this._frameSize = $common.getSize(this._contentFrame);
    },
    _onResizeEnded: function() {
        this._frameSize = null;
    },
    _onResizing: function(sender, offset) {
        $common.setSize(this._contentFrame, {
            width: this._frameSize.width + offset.width,
            height: this._frameSize.height + offset.height
        });
    }
}
Page.ModalDialog.registerClass('Page.ModalDialog', Page.Dialog);

Page.Prompt = function() {
    Page.Prompt.initializeBase(this);
    this._container = null;
    this._buttonContainer = null;
    this._contentDiv = null;
    this._buttonOk = null;
    this._buttonCancel = null;
    this._captionLabel = null;
    this._inputText = null;
    this._context = null;

    this._cancelClickHandler = null;
    this._textChangedHandler = null;
    this._selectStartHandler = null;
    this._keypressHandler = null;
    this._okClickHandler = null;
}

Page.Prompt.prototype = {
    initialize: function() {
        if (window.top == window) {
            Page.Prompt.callBaseMethod(this, 'initialize');
        }
    },
    dispose: function() {
        if (window.top == window) {
            if (this._cancelClickHandler) {
                $removeHandler(this._buttonCancel, 'click', this._cancelClickHandler);
                this._cancelClickHandler = null;
            }
            if (this._okClickHandler) {
                $removeHandler(this._buttonOk, 'click', this._okClickHandler);
                this._okClickHandler = null;
            }
            if (this._textChangedHandler) {
                $removeHandler(this._inputText, 'keyup', this._textChangedHandler);
                this._textChangedHandler = null;
            }
            if (this._keypressHandler) {
                $removeHandler(this._inputText, 'keypress', this._keypressHandler);
                this._keypressHandler = null;
            }
            if (this._selectStartHandler) {
                $removeHandler(this._inputText, 'selectstart', this._selectStartHandler);
                this._selectStartHandler = null;
            }
            Page.Prompt.callBaseMethod(this, 'dispose');
        }
    },
    get_resizable: function() {
        return true;
    },
    createContent: function(parent) {
        this._container = $common.createElementFromTemplate({
            nodeName: 'Div'
        }, parent);
        this._contentDiv = $common.createElementFromTemplate({
            nodeName: 'Div',
            properties: {
                className: 'PromptContent'
            }
        }, this._container);
        this._captionLabel = $common.createElementFromTemplate({
            nodeName: 'SPAN'
        }, $common.createElementFromTemplate({
            nodeName: 'Div'
        }, this._contentDiv));
        this._inputText = $common.createElementFromTemplate({
            nodeName: 'INPUT',
            properties: {
                type: 'text'
            }
        }, this._contentDiv);
        this._buttonContainer = $common.createElementFromTemplate({
            nodeName: 'DIV',
            properties: {
                className: 'MessageButton'
            }
        }, this._container);
        this._buttonOk = $common.createElementFromTemplate({
            nodeName: 'INPUT',
            properties: {
                type: 'submit',
                value: 'OK',
                className: 'messageButton'
            }
        }, this._buttonContainer);
        this._buttonCancel = $common.createElementFromTemplate({
            nodeName: 'INPUT',
            properties: {
                type: 'submit',
                value: 'Cancel',
                className: 'messageButton'
            }
        }, this._buttonContainer);
        return this._container;
    },
    show: function(caption, value, callback) {
        this._context = { callback: callback, caption: caption, value: value };
        Page.Prompt.callBaseMethod(this, 'show');
    },
    onShowing: function() {
        $text(this._captionLabel, typeof (this._context.caption) === 'undefined' || this._context.caption === null ? '' : this._context.caption);
        $text(this._inputText, this._context.value);
        $common.setStyle(this._contentDiv, { width: '340px', height: '50px' });

        if (!this._cancelClickHandler) {
            this._cancelClickHandler = Function.createDelegate(this, this._onCancel);
            $addHandler(this._buttonCancel, 'click', this._cancelClickHandler);
        }
        if (!this._okClickHandler) {
            this._okClickHandler = Function.createDelegate(this, this._onOk);
            $addHandler(this._buttonOk, 'click', this._okClickHandler);
        }
        if (!this._textChangedHandler) {
            this._textChangedHandler = Function.createDelegate(this, this._onTextChange);
            $addHandler(this._inputText, 'keyup', this._textChangedHandler);
        }
        if (!this._keypressHandler) {
            this._keypressHandler = Function.createDelegate(this, this._onKeyPress);
            $addHandler(this._inputText, 'keypress', this._keypressHandler);
        }
        if (!this._selectStartHandler) {
            this._selectStartHandler = Function.createDelegate(this, this._onSelectStart);
            $addHandler(this._inputText, 'selectstart', this._selectStartHandler);
        }
    },
    onShown: function() {
        $common.setSize(this._contentDiv, {
            width: Math.max(this._contentDiv.scrollWidth, this._contentDiv.offsetWidth),
            height: Math.max(this._contentDiv.offsetHeight, this._contentDiv.scrollHeight)
        });
        this._layout();
        $common.setSize(this._container, {
            width: this._contentDiv.offsetWidth,
            height: this._contentDiv.offsetHeight + this._buttonContainer.offsetHeight
        });
        this._invalidate();
        this._inputText.focus();
        Page.setSelectionRange(this._inputText, 0, this._inputText.value.length);
    },
    onHidden: function() {
        this._context = null;
        this.dispose();
    },
    _onCancel: function(evt) {
        evt.preventDefault();
        this.hide();
    },
    _onOk: function(evt) {
        evt.preventDefault();
        if (!this._context.callback || (this._context.callback && this._context.callback(this, $text(this._inputText)))) {
            this.hide();
        }
    },
    _onTextChange: function(evt) {
        this._invalidate();
    },
    _onKeyPress: function(evt) {
        if (evt.charCode == 13) {
            this._buttonOk.click();
        }
    },
    _onSelectStart: function(evt) {
        evt.stopPropagation();
        return true;
    },
    _invalidate: function() {
        $common.applyProperties(this._buttonOk, { disabled: ($text(this._inputText) ? '' : 'disabled') });
    }
}
Page.Prompt.registerClass('Page.Prompt', Page.Dialog);
